Memra

The Throwable hierarchy: checked vs unchecked

How Java classifies exceptions — and why the checked/unchecked distinction matters.

The exception hierarchy

Every problem Java can throw is a subclass of Throwable:

Throwable
├── Error            ← don't catch (JVM-level: OutOfMemoryError, StackOverflowError)
└── Exception
    ├── RuntimeException       ← unchecked
    │   ├── NullPointerException
    │   ├── IllegalArgumentException
    │   ├── ClassCastException
    │   └── ...
    └── IOException            ← checked
    └── SQLException           ← checked
    └── ... (checked)

Checked exceptions — everything that extends Exception but NOT RuntimeException. The compiler enforces the handle-or-declare rule: if a method can throw a checked exception, callers must either catch it or declare it with throws.

Unchecked exceptionsRuntimeException and its subclasses, plus Error. The compiler doesn't force you to handle them. They signal programming errors (NullPointerException, ArrayIndexOutOfBoundsException) or unrecoverable JVM failures (Error).

The basic try/catch/finally structure:

try {
    String text = Files.readString(Path.of("data.txt"));  // throws IOException
    System.out.println(text);
} catch (IOException e) {
    System.err.println("Read failed: " + e.getMessage());
} finally {
    System.out.println("this always runs");
}

finally runs regardless: whether the try completes normally, an exception is caught, or an exception is uncaught and propagates. The only exceptions are JVM abort (System.exit) and Error-level crashes.

NORMAL ~/memra/learn/java-from-zero/throwable-hierarchy utf-8 LF