The Throwable hierarchy in Java
Throwable → Error / Exception → RuntimeException.
catch (Exception e) to catch EVERYTHING. Then the program crashes anyway. What slipped through the net?A family tree of trouble
Everything you can throw extends **Throwable. It splits into two branches: Error and Exception. Under Exception sits RuntimeException**, the root of the unchecked exceptions.
Throwable
├── Error
│ └── StackOverflowError
└── Exception
├── IOException
└── RuntimeException
└── NullPointerExceptionTwo very different branches
Error: serious trouble in the JVM or environment, like OutOfMemoryError — usually not something to handle. Exception: conditions a program may handle. Errors and RuntimeExceptions are unchecked; other Exceptions, like IOException, are checked. Families nest deep: NumberFormatException → IllegalArgumentException → RuntimeException.
Where does it sit?
What does this print?
Throwable t = new ClassCastException();
System.out.println(t instanceof RuntimeException);
System.out.println(t instanceof Error);true falsefalse falsetrue true
Show the answer
ClassCastException extends RuntimeException, which extends Exception. It's on the Exception branch, so it is never an Error.
catch (Exception e) is not a net for everything
Errors live on the other branch. catch (Exception e) does NOT catch a StackOverflowError — it escapes, and the line after the try never runs.
try {
throw new StackOverflowError();
} catch (Exception e) { // no match!
System.out.println("caught");
}Who gets checked?
Which of these does the compiler force you to handle: IllegalStateException, AssertionError, IOException, ArithmeticException?
Think about it, then reveal the answer
Only **IOException** — it's a checked Exception. Two of the others extend RuntimeException, and AssertionError is an Error: all unchecked.
Why the tree matters
In production, monitoring tools group crashes by exception type, and catch blocks decide what to handle by family. Knowing the tree tells you that a top-level catch (Exception e) in a web server will log bad requests — but won't catch an OutOfMemoryError killing the process.
Key takeaways
- Throwable → Error and Exception
- Exception → RuntimeException (unchecked) and others like IOException (checked)
- Errors and RuntimeExceptions are unchecked
- catch (Exception e) does NOT catch Errors
throw new Throwable() is legal — and Throwable itself counts as checked, so the method must declare throws Throwable. Only the Error and RuntimeException subtrees are unchecked.
Practice questions
What does this print?
Throwable t = new NumberFormatException();
System.out.println(t instanceof IllegalArgumentException);
System.out.println(t instanceof RuntimeException);
System.out.println(t instanceof Error);- true true false
- false true false
- true false false
- false false false
Check your answer
true true false. NumberFormatException extends IllegalArgumentException, which extends RuntimeException. It's on the Exception branch, not Error.
What does this print?
try {
throw new StackOverflowError();
} catch (Exception e) {
System.out.println("caught");
}
System.out.println("after");- caught after
- after
- Throws StackOverflowError
- caught
Check your answer
Throws StackOverflowError. StackOverflowError is an Error, not an Exception, so catch (Exception e) doesn't match and the error escapes main.