W3docs

Java Exception Hierarchy

How Throwable, Error, Exception, and RuntimeException relate in Java's exception class hierarchy.

Java Exception Hierarchy

Every exception in Java is part of a small class tree rooted at java.lang.Throwable. Knowing the shape of that tree pays off constantly: it explains why catch (Exception e) doesn't catch OutOfMemoryError, why RuntimeException is special, and why some exceptions force you to handle them while others don't. The whole layout fits in one diagram.

The whole tree

Throwable
├── Error                       (unchecked — JVM-level)
│   ├── OutOfMemoryError
│   ├── StackOverflowError
│   ├── VirtualMachineError
│   └── ...
└── Exception                   (checked by default)
    ├── IOException             (checked)
    ├── SQLException            (checked)
    ├── ClassNotFoundException  (checked)
    ├── ...
    └── RuntimeException        (unchecked)
        ├── NullPointerException
        ├── IllegalArgumentException
        ├── IndexOutOfBoundsException
        ├── ArithmeticException
        ├── ClassCastException
        ├── IllegalStateException
        └── ...

The whole tree is one class hierarchy. That's why a catch for a supertype catches its subtypes, why exception variables behave like ordinary references, and why you can hold an IOException in an Exception-typed field.

Throwable — the root

Throwable is what throw accepts and what catch declares. Anything you'd want to raise or handle is a subclass of Throwable. The class itself provides:

  • A message (getMessage())
  • A stack trace captured at construction (getStackTrace(), printStackTrace())
  • An optional cause — another Throwable that triggered this one (getCause())
  • Suppressed exceptions — secondary failures attached by try-with-resources (getSuppressed())

You almost never extend Throwable directly. The interesting design lives one level below.

Error — don't catch

Error and its subclasses represent failures the JVM signals: out of memory, stack overflow, a class file that can't be linked. By convention you do not catch them in application code, because:

  1. They usually mean the JVM is no longer reliable. Continuing past an OutOfMemoryError rarely works for long.
  2. There's almost never a sensible recovery action your code can take.
  3. The JVM itself may already be doing something about them; intercepting interferes.

Error is technically catchable — Java doesn't stop you. But the convention is so strong that "catches Error" is treated as a code-review red flag. The only legitimate use case is a top-level supervisor (a request handler, a job runner) that logs and exits cleanly.

Exception — application failures

Everything other than Error under Throwable is Exception or one of its subtypes. The line between checked and unchecked runs inside this branch, not above it:

  • Direct subclasses of Exception that aren't RuntimeException are checked.
  • RuntimeException and all its subtypes are unchecked.

That's why catch (Exception e) matches both IOException (checked) and NullPointerException (unchecked) — they're siblings under the same root. It's also why catching Exception is so blunt: you've thrown both branches together.

RuntimeException — the bug branch

RuntimeException and its subtypes are reserved by convention for programming errors that shouldn't happen in correct code:

  • NullPointerException — dereferencing null
  • IllegalArgumentException — bad argument
  • IllegalStateException — wrong state for the operation
  • IndexOutOfBoundsException — list/array index past the end
  • ArithmeticException — divide by zero
  • ClassCastException — bad cast
  • UnsupportedOperationException — operation not supported (e.g. mutating an unmodifiable list)

You can throw these from anywhere without changing your method signature. Callers may catch them, but the language doesn't make them. They're the right tool when the failure says "this is a bug" rather than "this happens sometimes."

Type relationships in catch

A catch (T e) matches any thrown value that is an instance of T or a subtype of T. So the hierarchy directly dictates what your catches see:

try { ... }
catch (IOException e)        { ... }   // catches FileNotFoundException too
catch (Exception e)          { ... }   // catches almost everything below Throwable
catch (Throwable t)          { ... }   // catches everything, including Error — don't

This is why a one-catch-all-the-things shape is dangerous. catch (Exception) catches NullPointerException (a bug) and IOException (a recoverable failure) and IllegalStateException (probably a bug) — all in one block, with no way to handle them differently. The hierarchy is asking you to be more specific.

Looking up types

When you encounter a new exception in a stack trace and want to know where it sits:

  • It's in java.lang if it's a fundamental error (NullPointerException, ArithmeticException).
  • It's in java.io, java.sql, java.net if it relates to that package's domain.
  • A class ending in Error is almost certainly under Error.
  • A class ending in Exception is almost certainly under Exception — but check whether it extends RuntimeException to know if it's checked.

The Javadoc shows the inheritance chain at the top of every page. When in doubt, look it up.

A worked example

A small program that walks the hierarchy with instanceof checks. It catches a sequence of throws as Throwable, then reports where each one sits in the tree.

java— editable, runs on the server

The isChecked helper encodes the rule in one line: the checked subset is Exception minus RuntimeException. Run the program and you'll see exactly which of the five sits where: IOException is checked, the two RuntimeExceptions aren't, the OutOfMemoryError is an Error (so it's neither an Exception nor checked), and the plain Exception is checked.

What's next

The built-in tree covers most cases. When your domain has its own failures — "invoice not found," "config out of date" — you write your own classes. Continue to Java custom exceptions.

Practice

Practice

A program does `catch (Exception e)` around a block. Which of these will be caught?