Course topics

By WebNest Studio Engineering Team

Java - Core Tutorial

Exception Hierarchy

Every exception and error in Java descends from a single root class: Throwable. Understanding this hierarchy explains why catch blocks match subclasses, why some exceptions must be declared and others don't, and why catching certain top-level types is considered poor practice.

Why it matters

A catch block catches the named type and every subclass of it, so the hierarchy decides which block handles which failure and in what order catch blocks must be written. It also separates the problems you should handle — exceptions — from the ones you generally cannot — errors such as running out of memory.

Throwable Error Exception OutOfMemoryError StackOverflowError unchecked; do not catch RuntimeException IOException NullPointerException unchecked checked (also SQLException)
Everything that can be thrown extends Throwable. Checked exceptions are the subclasses of Exception outside the RuntimeException branch.

The Throwable Tree

Throwable sits at the top and has two direct subclasses: Exception and Error. Exception is further split by whether it extends RuntimeException or not — anything extending RuntimeException is unchecked, and everything else under Exception is checked. This gives Java exceptions a clean three-branch structure.

  • Throwable — the root of everything that can be thrown or caught.
  • Throwable → Error — serious, usually unrecoverable JVM-level problems (e.g. OutOfMemoryError, StackOverflowError).
  • Throwable → Exception — recoverable application-level problems, split into checked and unchecked.
  • Exception → RuntimeException — the unchecked branch (e.g. NullPointerException, ArithmeticException, ClassCastException).
  • Exception (direct subclasses outside RuntimeException) — the checked branch (e.g. IOException, SQLException).

Why Error Should Generally Not Be Caught

Error and its subclasses represent conditions a normal application is not expected to recover from, typically involving the JVM itself running out of a critical resource or reaching an invalid internal state — running out of heap memory, or a stack that has overflowed from runaway recursion. Catching an Error and trying to "handle" it usually just delays an inevitable failure while masking the real problem, and can leave the JVM in an inconsistent state. Catching Throwable broadly is discouraged for the same reason: it silently swallows Errors alongside genuine exceptions, hiding serious problems instead of surfacing them.

Why the Hierarchy Matters for catch Blocks

Because catch blocks match by type — including any subclass — a catch block for a superclass will also catch every subclass beneath it in the tree. Catching Exception broadly will catch both checked exceptions and every RuntimeException, which is why catch ordering rules (specific before general) exist, and why overly broad catches are usually a design smell: they hide exactly which failure occurred.

Example

Catching a subclass, its checked/unchecked superclass, and observing hierarchy-based matching

Java
public class HierarchyDemo {
    public static void main(String[] args) {
        try {
            int[] data = new int[3];
            System.out.println(data[10]); // throws ArrayIndexOutOfBoundsException
        } catch (RuntimeException e) {
            // Catches it because ArrayIndexOutOfBoundsException
            // extends IndexOutOfBoundsException extends RuntimeException
            System.out.println("Caught via RuntimeException: " + e.getClass().getSimpleName());
        }

        try {
            Object obj = "text";
            Integer number = (Integer) obj; // throws ClassCastException
        } catch (Exception e) {
            System.out.println("Caught via Exception: " + e.getClass().getSimpleName());
        }
    }
}
Output
Caught via RuntimeException: ArrayIndexOutOfBoundsException
Caught via Exception: ClassCastException

Common Mistakes

  • Writing catch (Throwable t) to "catch everything," which also silently swallows Errors like OutOfMemoryError instead of letting the JVM report them.
  • Assuming RuntimeException and Error are siblings under Exception — Error is actually a separate branch directly under Throwable, not under Exception at all.
  • Catching a generic Exception when a specific subclass was intended, hiding which real problem occurred and making debugging harder.
  • Trying to recover from a StackOverflowError or OutOfMemoryError as if it were an ordinary exception, instead of treating it as a signal to fix the root cause.

Exercise

This code does not compile: the general catch (Exception e) comes first, so the specific catch below it can never be reached. Reorder the catch blocks.

Expected output: Arithmetic problem

Java · starter code
public class CatchOrder {
    public static void main(String[] args) {
        try {
            int result = 10 / 0;
            System.out.println(result);
        } catch (Exception e) {
            System.out.println("General problem");
        } catch (ArithmeticException e) {
            System.out.println("Arithmetic problem");
        }
    }
}
Show hints
  • ArithmeticException is a subclass of Exception, so the first block already catches it.
  • List catch blocks from the most specific type to the most general.
Show solution
Java · solution
public class CatchOrder {
    public static void main(String[] args) {
        try {
            int result = 10 / 0;
            System.out.println(result);
        } catch (ArithmeticException e) {
            System.out.println("Arithmetic problem");
        } catch (Exception e) {
            System.out.println("General problem");
        }
    }
}

Quick quiz

1. Which class is at the top of the hierarchy for everything that can be thrown?

  • A. Exception
  • B. Throwable
  • C. Error
  • D. RuntimeException
Show answer

B. Throwable is the root. Its two direct subclasses are Error and Exception.

2. OutOfMemoryError is a subclass of which class?

  • A. Exception
  • B. RuntimeException
  • C. Error
  • D. IOException
Show answer

C. It belongs to the Error branch, which represents serious problems an application is not expected to handle.

3. Which class does RuntimeException extend directly?

  • A. Throwable
  • B. Error
  • C. Object
  • D. Exception
Show answer

D. RuntimeException is a subclass of Exception, so catch (Exception e) also catches every runtime exception.

Interview questions

What is the difference between an Error and an Exception?

Show answer

Both extend Throwable. An Error signals a serious problem in the JVM or the environment, such as OutOfMemoryError or StackOverflowError; an application normally cannot recover and should not try to catch it. An Exception represents a condition an application might reasonably handle, such as a missing file or invalid input.

Is it a good idea to catch Throwable or Exception?

Show answer

Rarely. catch (Throwable t) also catches errors such as running out of memory, which you cannot usefully handle. catch (Exception e) swallows unrelated bugs such as a NullPointerException along with the failure you expected. Catch the most specific type you can deal with. A broad catch is acceptable only at the top level of an application, to log the failure and stop cleanly.

Key Points to Remember

  • Throwable is the root; its two direct branches are Exception and Error.
  • Exception splits further into unchecked (RuntimeException and its subclasses) and checked (everything else under Exception).
  • A catch block matches its declared type and every subclass beneath it in the hierarchy.
  • Error represents serious JVM-level problems and should generally not be caught or "handled" like an ordinary exception.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Runs plain Java 17. Frameworks such as Spring, and databases, are not available here. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering