Back to Exception Hierarchy
Q2Question 2 of 2

What is the Throwable class and the exception hierarchy?

Key Concepts
  • Throwable is the root: only Throwable and its subclasses can be thrown or caught
  • Error: serious JVM problems (OutOfMemoryError, StackOverflowError) – do not catch these
  • Exception: recoverable problems. Two types: checked (must handle) and unchecked (RuntimeException)
  • Checked exceptions: IOException, SQLException – compiler forces try-catch or throws declaration
  • Unchecked exceptions: NullPointerException, IllegalArgumentException – compiler does not force handling
  • Best practice: throw specific exceptions, catch specific exceptions, never catch Throwable or Error

Definition

Throwable is the root of all exceptions. It branches into Error (JVM problems, do not catch) and Exception (application problems). Exception branches into checked (compile-time enforced) and unchecked (RuntimeException).

The hierarchy:

  • Throwable – root class. Only Throwable subclasses can be thrown.
  • Error – serious JVM failures: OutOfMemoryError, StackOverflowError. Applications should not catch these.
  • Exception – application-level problems.
  • Checked (non-Runtime): IOException, SQLException. Compiler enforces handling.
  • Unchecked (RuntimeException): NullPointerException, IllegalArgumentException. Compiler does not enforce.

Errors are like a bank building catching fire (OutOfMemoryError) – you evacuate, you do not try to continue business. Checked exceptions are like a customer's cheque bouncing (IOException) – you must handle it with a specific procedure. Unchecked exceptions are like a teller entering a wrong amount (NullPointerException) – a programming mistake that should be fixed, not caught.

Understanding the Concepts

What does the hierarchy look like?

DIAGRAM
java.lang.Object
  └── java.lang.Throwable
        ├── java.lang.Error                    (do not catch)
        │     ├── OutOfMemoryError
        │     ├── StackOverflowError
        │     └── NoClassDefFoundError
        └── java.lang.Exception                (catch and handle)
              ├── IOException                   (checked)
              ├── SQLException                  (checked)
              ├── InterruptedException           (checked)
              └── java.lang.RuntimeException    (unchecked)
                    ├── NullPointerException
                    ├── IllegalArgumentException
                    ├── IndexOutOfBoundsException
                    ├── ClassCastException
                    └── ArithmeticException

What is the difference between checked and unchecked?

JAVA
// CHECKED: compiler forces you to handle it
public void readFile() throws IOException {    // must declare
    BufferedReader reader = new BufferedReader(new FileReader("accounts.csv"));
    // ...
}

// Or catch it:
public void readFileSafe() {
    try {
        BufferedReader reader = new BufferedReader(new FileReader("accounts.csv"));
    } catch (IOException e) {
        log.severe("File not found: " + e.getMessage());
    }
}

// UNCHECKED: compiler does not force handling
public void process(Account account) {
    account.getBalance();  // throws NPE if account is null – no compiler warning
}

| Feature | Checked | Unchecked |
|---|---|---|
| Superclass | Exception (not RuntimeException) | RuntimeException |
| Compiler enforces | Yes (try-catch or throws) | No |
| Examples | IOException, SQLException | NullPointerException, IllegalArgumentException |
| Represents | External failures (I/O, network, DB) | Programming bugs (null, bad args) |
| Handling | Recover or propagate | Fix the code |

When should you use checked vs unchecked in your own code?

JAVA
// Checked: caller CAN recover from this
public class InsufficientFundsException extends Exception {
    public InsufficientFundsException(String message) {
        super(message);
    }
}

// Unchecked: programming error, caller should fix the code
public class InvalidAccountIdException extends RuntimeException {
    public InvalidAccountIdException(String message) {
        super(message);
    }
}

What should you NEVER catch?

JAVA
// NEVER catch Error – JVM is in an unrecoverable state
try {
    // ...
} catch (OutOfMemoryError e) {  // BAD: JVM cannot allocate memory
    // What can you do here? Nothing reliable.
}

// NEVER catch Throwable – catches both Error and Exception
try {
    // ...
} catch (Throwable t) {  // BAD: hides OutOfMemoryError, StackOverflowError
}

What are common mistakes?

  • Catching Exception broadly. catch (Exception e) catches everything including NPE. Catch specific exceptions.
  • Empty catch block. catch (IOException e) { } silently swallows the error. Always log.
  • Using checked exceptions for programming errors. NPE-style bugs should be unchecked. Checked exceptions are for recoverable external failures.
  • Catching Error. The JVM is broken. Let it crash and restart.

What should a senior engineer know?

  • Multi-catch (Java 7+): catch (IOException | SQLException e) catches either type.
  • try-with-resources (Java 7+): auto-closes resources. try (var conn = getConnection()) { ... }.
  • Suppressed exceptions: When try-with-resources auto-close throws after the try block threw, the close exception is suppressed. Access via e.getSuppressed().
  • Spring @Transactional rolls back on unchecked exceptions by default. Checked exceptions do not trigger rollback unless configured.

Visual Overview

DIAGRAM
  Throwable
  ├── Error (do NOT catch)
  │     OutOfMemoryError, StackOverflowError
  └── Exception
        ├── Checked (MUST handle)
        │     IOException, SQLException
        │     → try-catch or throws declaration
        └── RuntimeException (unchecked, no forced handling)
              NullPointerException, IllegalArgumentException
              → fix the code, do not catch

Key Takeaway

Throwable is the root of Java's exception hierarchy. It branches into Error (JVM failures you should not catch) and Exception (application problems you should handle). Exception further branches into checked exceptions (compiler-enforced, for recoverable external failures) and unchecked RuntimeExceptions (for programming bugs).

Catch specific exceptions, never Throwable or Error. Use checked exceptions when the caller can recover. Use unchecked exceptions for bugs that should be fixed in the code. And always log caught exceptions – never swallow them silently.

One sentence to remember: Error is fatal, checked Exception is recoverable, RuntimeException is a bug – catch specific, never broad.

---

Share
Use ← → arrow keys to navigate