Java Exception Hierarchy: Throwable, Error and Stack Traces
Every Java exception extends Throwable, which splits into Error and Exception, checked or unchecked. The common exceptions and how to read a stack trace.
- Course: Java study plan
- Module: Exceptions
- Kind: Lesson
- Reading time: 13 min
- Runtime: OpenJDK 18
What is the exception hierarchy in Java?
The Java exception hierarchy is rooted at Throwable, which has two branches. Error covers failures of the JVM itself, such as OutOfMemoryError, which programs should not catch. Exception covers conditions a program may handle: its subclasses are checked, so callers must catch or declare them, except RuntimeException and its subclasses, which are unchecked.
Lesson
When something goes wrong at run time — an index out of range, a file missing, a null dereferenced — Java creates an exception object describing the failure and unwinds the call stack until something catches it. Every exception is an instance of a class in one hierarchy rooted at Throwable, and where a class sits in that hierarchy decides whether the compiler forces you to handle it. This lesson is the map of that hierarchy, the exceptions you will meet daily, and how to read the stack trace you get when one escapes.
The hierarchy
Throwable
├── Error — the JVM is in trouble; do not catch
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ ├── NoClassDefFoundError
│ └── AssertionError
└── Exception — something a program might recover from
├── IOException — CHECKED: must be declared or caught
│ ├── FileNotFoundException
│ └── UncheckedIOException (RuntimeException, despite the name's neighbourhood)
├── SQLException — checked
├── InterruptedException — checked
├── ReflectiveOperationException (ClassNotFoundException, …) — checked
└── RuntimeException — UNCHECKED: may be thrown anywhere without declaration
├── NullPointerException
├── IllegalArgumentException
│ └── NumberFormatException
├── IllegalStateException
├── IndexOutOfBoundsException
│ ├── ArrayIndexOutOfBoundsException
│ └── StringIndexOutOfBoundsException
├── ArithmeticException
├── ClassCastException
├── UnsupportedOperationException
├── ConcurrentModificationException
├── NoSuchElementException
└── DateTimeException
Three tiers to remember:
Error: the JVM or the platform failed — memory exhausted, stack blown, a class missing at load time. Programs should not catch these (what would you do?), and catchingThrowableto "be safe" swallows them.- Checked exceptions (
Exceptionand subclasses that are notRuntimeException): conditions a correct program should anticipate — a file may be absent, a network may drop. The compiler forces every caller to catch or declare them. - Unchecked exceptions (
RuntimeExceptionand subclasses): programming errors and precondition violations — null where an object was required, an index past the end, an argument out of range. No declaration required; they propagate freely.
The next lesson is about the checked/unchecked decision; here, the key is that the class of an exception is the category of failure.
What an exception carries
Throwable t;
t.getMessage() // the detail message given at construction, or null
t.getCause() // the exception that caused this one, or null (chaining — lesson 5)
t.getStackTrace() // StackTraceElement[] — the frames at the point of creation
t.printStackTrace() // to System.err; loggers do this properly
t.getSuppressed() // exceptions suppressed during try-with-resources cleanup (lesson 4)
t.toString() // "java.lang.IllegalArgumentException: the message"
The stack trace is captured when the object is constructed — not when it is thrown. That is why new Exception() is expensive (filling the trace walks the stack) and why creating exceptions for control flow is slow (lesson 6).
Reading a stack trace
Exception in thread "main" java.lang.NumberFormatException: For input string: "4.2"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
at java.base/java.lang.Integer.parseInt(Integer.java:661)
at java.base/java.lang.Integer.parseInt(Integer.java:777)
at com.example.Parser.readAge(Parser.java:42)
at com.example.Parser.parse(Parser.java:18)
at com.example.Main.main(Main.java:9)
Read it this way:
- The first line: the thread, the exception class, the message. Here the message tells you the bad input exactly.
- The frames, top to bottom: innermost first. The top frames are usually library code that detected the problem; scan down to the first frame in your package —
Parser.readAge(Parser.java:42)— that is where to look. The frames below show how execution got there. Caused by:sections (chained exceptions) follow, each with its own trace, and... 12 moremeans frames identical to the enclosing trace were omitted.
A stack trace is a gift: it names the exact line. Learn to jump to the first frame of your own code rather than reading from the top.
The common unchecked exceptions, and what they mean
| Exception | What it says about your code |
|---|---|
NullPointerException | A null reference was dereferenced. Since Java 14 the message says which one: Cannot invoke "String.length()" because "name" is null. |
ArrayIndexOutOfBoundsException | An index outside 0..length-1; the message gives index and length. |
StringIndexOutOfBoundsException | charAt/substring out of range. |
IllegalArgumentException | A method received an argument it cannot accept — thrown by you in validation. |
IllegalStateException | The object was not in a state that allows the call (Iterator.remove twice, a closed stream). |
NumberFormatException | parseInt/parseDouble on non-numeric text. |
ArithmeticException | Integer division by zero; BigDecimal non-terminating division. |
ClassCastException | A downcast to a class the object is not. |
UnsupportedOperationException | An optional operation not supported by this implementation (List.of(…).add). |
ConcurrentModificationException | A collection was modified during iteration. |
NoSuchElementException | next() past the end, Optional.get() on empty, Scanner with no token. |
Most of these mean a bug in the calling code, not a condition to catch: fix the null, the index, the argument. The exception is the diagnosis.
Throwable versus Exception versus RuntimeException in a catch
catch (Exception e) catches every checked and unchecked exception but not Errors; catch (RuntimeException e) only unchecked; catch (Throwable t) everything including OutOfMemoryError. The last belongs only at the very top of a thread (a framework's request loop) where the intent is to log and keep the process alive — and even there, Errors are usually rethrown.
Exceptions are objects
You can store them, pass them, inspect them, and construct them without throwing. throw needs a Throwable expression: throw new IllegalStateException("closed"). The throw statement ends the method's normal execution immediately; code after it in the block is unreachable.
Interview angle
- "Difference between
ErrorandException?"Errors are JVM-level failures you do not catch;Exceptions are conditions a program may handle. - "Checked vs unchecked — which classes?" Unchecked =
RuntimeExceptionand its subclasses (andErrors); everything else underExceptionis checked. - "Is
NullPointerExceptionchecked?" No — it extendsRuntimeException. - "When is the stack trace captured?" At construction, in
Throwable's constructor. - "What does
... 12 moremean?" Frames shared with the enclosing trace were elided.
Key takeaways
Throwable→Error(do not catch) andException→ checked (compiler-enforced) vsRuntimeException(unchecked).- Unchecked exceptions usually mean a bug: fix the cause rather than catching.
- Read a stack trace by finding the first frame in your own code; the message often names the bad value.
- An exception is an object with a message, a cause and a trace captured at construction.
Common questions
What is the difference between Error and Exception in Java?
An Error signals that the JVM or platform failed — memory exhausted, stack overflowed, a class missing at load time — and programs should not catch it. An Exception is a condition a program might anticipate and recover from, such as a missing file or a bad argument.
Is NullPointerException checked or unchecked?
Unchecked. NullPointerException extends RuntimeException, so no method has to declare or catch it. It signals a bug — a null reference was dereferenced — and since Java 14 its message names which reference was null.
How do you read a Java stack trace?
Start with the first line: the thread, the exception class and the message, which often names the bad value. Then scan the frames, innermost first, down to the first one in your own package — that is the line to look at. Caused by: sections follow for chained exceptions.
What does "... 12 more" mean in a Java stack trace?
It means that many frames identical to the enclosing trace were left out. It appears in Caused by: sections, whose lower frames are usually shared with the exception that wrapped them.
When is an exception's stack trace captured in Java?
When the exception object is constructed, in Throwable's constructor — not when it is thrown. Filling in the trace walks the stack, which is why creating exceptions is relatively expensive and why they should not be used for normal control flow.
Exercises
Name that exception
Read an integer n and n commands, each of which triggers a runtime exception: parse <text> (Integer.parseInt), index <i> (index into a 3-element array), div <a> <b> (integer division), cast (cast a String held in an Object to Integer), nullcall (call length() on a null String). Catch each exception as RuntimeException and print <simple class name>: <message>; if no exception occurs print ok <result>.
Example: 3 then parse 42, parse x, div 1 0 →
ok 42
NumberFormatException: For input string: "x"
ArithmeticException: / by zeroCatch the specific one first
Read n commands. parse <text> calls Integer.parseInt; divide <a> <b> computes a / b; index <i> reads element i of a three-element array {10, 20, 30}. Run each inside one try with catch blocks ordered most specific first: NumberFormatException → bad number: <text>, ArithmeticException → arithmetic: <message>, ArrayIndexOutOfBoundsException → index: <message>, and a final RuntimeException → other: <simple class name>. On success print the result.
Example: 4 then parse 42, parse x, divide 1 0, index 5 →
42
bad number: x
arithmetic: / by zero
index: Index 5 out of bounds for length 3In this module: Exceptions
- The exception hierarchy and how to read a stack trace (this lesson)
- Checked versus unchecked — the rules and the judgement
- try, catch and finally — the exact semantics
- try-with-resources and AutoCloseable
- Custom exceptions and exception chaining
- Exception handling that survives production
- Checkpoint — Exceptions
← Checkpoint — Nested classes & lambdas · Checked versus unchecked — the rules and the judgement →