💥 Exceptions & Errors · Intermediate

Common runtime exceptions in Java

NPE, ClassCast, IllegalArgument, IllegalState, NumberFormat, IndexOutOfBounds, UnsupportedOperation, ConcurrentModification.

🧩 The mysteryMost Java crashes come from the same handful of exceptions. Learn their names and each one tells you what went wrong before you read a single line of code.

The bad-data family

NullPointerException: you used null. ClassCastException: an impossible cast. NumberFormatException: text that isn't a number. IndexOutOfBoundsException and its kids, like StringIndexOutOfBoundsException: an index past the end.

Integer.parseInt("abc"); // NumberFormat
String s = null;
s.length();              // NullPointer
"hi".charAt(5);          // StringIndexOutOfBounds
Object o = 1;
String t = (String) o;   // ClassCast

The API-misuse family

IllegalArgumentException: the value passed in is bad. IllegalStateException: the call is fine, but the object's state isn't — e.g. using it after it was closed. UnsupportedOperationException: this object doesn't support that operation. ConcurrentModificationException: a collection changed while being iterated.

🔮 Predict it

Changing a fixed list

What happens?

List<String> tags = List.of("a", "b");
tags.add("c");
System.out.println(tags);
  1. [a, b, c]
  2. Compile error
  3. Throws UnsupportedOperationException
Show the answer

List.of returns an unmodifiable list. The List interface has add (and set), so it compiles — but the implementation refuses at runtime.

⚠️ The trap

Removing inside for-each

A for-each loop secretly uses an iterator. Removing through the list behind its back makes the next step fail fast with ConcurrentModificationException. Use nums.removeIf(n -> n == 1) instead.

List<Integer> nums =
    new ArrayList<>(List.of(1, 2, 3));
for (Integer n : nums) {
    if (n == 1) nums.remove(n);  // throws
}
🤔 Think first

Argument or state?

setDiscount(int percent) is called with 150. Which exception should it throw?

Think about it, then reveal the answer

IllegalArgumentException — the value passed in is out of range, so the argument is at fault. Throw it early with a clear message like "percent must be 0-100". IllegalStateException is for when the object's *current state* makes the call invalid.

💼 In the real world

Names point to fixes

In production logs, each name hints at the fix: NPE → validate earlier or handle the missing value; ConcurrentModificationException → removeIf or an iterator; UnsupportedOperationException → copy into a new ArrayList<>(...) before changing it.

Key takeaways

  1. IllegalArgumentException = bad input; IllegalStateException = wrong time/state
  2. List.of(...) is unmodifiable → UnsupportedOperationException on add/set
  3. Removing in a for-each loop → ConcurrentModificationException
  4. Helpful NPE messages (default since Java 15) name what was null
🤯 Did you know?

Since Java 15, NullPointerException messages name exactly what was null — like Cannot invoke "String.length()"… (added in Java 14, on by default in 15).

Practice questions

What does this print?

List<Integer> nums = List.of(1, 2, 3);
nums.set(0, 9);
System.out.println(nums);
  1. [9, 2, 3]
  2. [1, 2, 3]
  3. Throws UnsupportedOperationException
  4. Compile error
Check your answer

Throws UnsupportedOperationException. List.of returns an unmodifiable list. The List interface allows set(), so it compiles, but the implementation refuses at runtime.

What does this print?

List<String> names =
    new ArrayList<>(List.of("a", "b", "c"));
for (String n : names) {
    if (n.equals("a")) names.remove(n);
}
System.out.println(names);
  1. [b, c]
  2. Throws ConcurrentModificationException
  3. [a, b, c]
  4. Throws IndexOutOfBoundsException
Check your answer

Throws ConcurrentModificationException. The for-each loop uses an iterator. Removing through the list behind its back makes the iterator's next() fail fast.

Next: OutOfMemoryError and StackOverflowError. You CAN catch them — so why shouldn't you?