5.1 Exception Hierarchy: Checked vs. Unchecked Exceptions

Key Takeaways

  • java.lang.Throwable serves as the common root of the Java exception hierarchy, branching into java.lang.Error for catastrophic JVM failures and java.lang.Exception for conditions applications can handle.
  • Checked exceptions extend Exception (excluding RuntimeException) and are enforced by the compiler via the handle-or-declare rule, requiring enclosing try-catch blocks or method throws declarations.
  • Unchecked exceptions comprise RuntimeException and its subclasses along with Error, representing programmatic logic flaws and fatal JVM states that do not require mandatory compile-time handling.
  • Overriding methods in subclasses can declare the same, narrower (subclass), fewer, or no checked exceptions compared to the overridden method, and can declare any unchecked exceptions without restriction.
Last updated: September 2026

5.1 Exception Hierarchy: Checked vs. Unchecked Exceptions

In Java, robust error handling is built around a strongly typed object hierarchy rooted at java.lang.Throwable. The Java compiler and Java Virtual Machine (JVM) strictly enforce how exceptions are declared, thrown, and handled. Understanding the structural boundaries between checked exceptions, unchecked runtime exceptions, and fatal JVM errors is one of the most heavily tested domains on the Oracle Certified Professional: Java SE 21 Developer (1Z0-830) exam.


The Throwable Class Hierarchy

Every object that can be thrown via the throw statement or caught within a catch block must be an instance of java.lang.Throwable or one of its subclasses. Attempting to throw or catch an object that does not extend Throwable (such as throw new String("Error");) causes an immediate compilation failure.

                           java.lang.Object
                                  │
                         java.lang.Throwable
                                  │
                 ┌────────────────┴────────────────┐
                 │                                 │
          java.lang.Error                java.lang.Exception
                 │                                 │
     ┌───────────┴───────────┐         ┌───────────┴───────────┐
     │ OutOfMemoryError      │         │ RuntimeException      │ (Unchecked)
     │ StackOverflowError    │         │  ├─ NullPointerExc    │
     │ NoClassDefFoundError  │         │  ├─ ClassCastExc      │
     │ ExceptionInInitError  │         │  ├─ IllegalArgumentExc│
     └───────────────────────┘         │  └─ IndexOutOfBounds  │
            (Unchecked)                ├───────────────────────┤
                                       │ Checked Exceptions    │
                                       │  ├─ IOException       │
                                       │  ├─ SQLException      │
                                       │  └─ ClassNotFoundExc  │
                                       └───────────────────────┘

The Throwable hierarchy branches immediately into two primary subtrees:

  1. java.lang.Error (Unchecked): Indicates serious system-level problems and hardware or JVM resource exhaustion that a normal application should not attempt to catch or handle.
  2. java.lang.Exception: Indicates conditions that a reasonable application might anticipate and recover from. This subtree is divided into:
    • Unchecked Exceptions (RuntimeException and its subclasses): Conditions representing programming bugs, logic flaws, and improper API usage.
    • Checked Exceptions (all subclasses of Exception that do not extend RuntimeException): Conditions external to the application logic that a well-written program must anticipate (e.g., missing files, broken network sockets, database outages).

Unrecoverable JVM Errors (java.lang.Error)

Errors extend java.lang.Error and represent abnormal conditions from which an application cannot realistically recover. Because they are unchecked, methods do not need to declare them in their throws clause, nor are developers expected to catch them.

Error TypeTriggering ConditionExam Significance
java.lang.OutOfMemoryErrorThe JVM heap is exhausted and the Garbage Collector cannot reclaim enough memory to allocate a new object.Often simulated on exams with unbounded array allocations (e.g., int[] arr = new int[Integer.MAX_VALUE];).
java.lang.StackOverflowErrorA thread's execution stack is exhausted, typically due to infinite or excessively deep recursive method invocations without a terminating base condition.Synchronous failure occurring on method call boundaries.
java.lang.NoClassDefFoundErrorThe Java compiler successfully compiled a class against a dependency, but the JVM ClassLoader cannot locate the .class file at runtime during dynamic resolution.Contrasts with ClassNotFoundException (which is a checked exception thrown during explicit reflection like Class.forName()).
java.lang.ExceptionInInitializerErrorAn unchecked runtime exception occurs while evaluating a static variable initializer or inside a static { ... } initialization block.The JVM wraps the originating unchecked exception inside ExceptionInInitializerError.
java.lang.InternalError / VirtualMachineErrorFatal internal JVM malfunction or resource limitation.Superclass of OutOfMemoryError and StackOverflowError.

The ExceptionInInitializerError Trap

A classic 1Z0-830 exam question presents a class where static field initialization or a static block throws an unchecked exception:

public class StaticInitFailure {
    static int[] values = new int[0];
    static int badAccess = values[5]; // Throws ArrayIndexOutOfBoundsException
    
    public static void main(String[] args) {
        System.out.println("Application started");
    }
}

When the JVM attempts to load and initialize StaticInitFailure, the ArrayIndexOutOfBoundsException occurs during class initialization. The JVM intercepts it, wraps it inside java.lang.ExceptionInInitializerError, and aborts execution. Execution never enters main().

[!NOTE] A static initialization block cannot throw checked exceptions directly unless they are caught inside the block itself. Uncaught checked exceptions in static initializers cause a compilation error.


Checked vs. Unchecked Exceptions

The fundamental distinction between checked and unchecked exceptions lies in compiler enforcement.

+-----------------------+-----------------------------------------------------------+
| Exception Category    | Compiler Enforcement & Handling Requirements              |
+-----------------------+-----------------------------------------------------------+
| Checked Exceptions    | Subject to the 'Handle-or-Declare' rule. The compiler     |
| (extends Exception    | verifies that every invocation capable of throwing a      |
|  excluding Runtime)   | checked exception is enclosed in a try-catch or declared  |
|                       | in the enclosing method's throws clause.                  |
+-----------------------+-----------------------------------------------------------+
| Unchecked Exceptions  | NOT enforced by the compiler. Methods may throw, catch,   |
| (extends              | or ignore them without any throws declaration or try-catch|
|  RuntimeException)    | block. Represents programming bugs and logic defects.     |
+-----------------------+-----------------------------------------------------------+

Common Checked Exceptions in java.base

  • java.io.IOException (and subclasses FileNotFoundException, EOFException)
  • java.sql.SQLException
  • java.lang.ClassNotFoundException (thrown by Class.forName(), ClassLoader.loadClass())
  • java.lang.InterruptedException (thrown when a thread sleeping or waiting is interrupted)
  • java.text.ParseException (thrown by DateFormat.parse())

Common Unchecked Exceptions (RuntimeException Subclasses)

  • java.lang.NullPointerException: Attempting to dereference a null object pointer.
  • java.lang.ArrayIndexOutOfBoundsException / IndexOutOfBoundsException: Accessing an index outside [0, size - 1].
  • java.lang.IllegalArgumentException: Passing an illegal or inappropriate argument to a method.
  • java.lang.IllegalStateException: Invoking an operation when the object is in an invalid state.
  • java.lang.ClassCastException: Attempting to cast an object reference to an incompatible type.
  • java.lang.ArithmeticException: Integer division or modulo by zero (10 / 0). (Note: Floating-point division by zero produces Infinity or NaN, not an ArithmeticException!).
  • java.lang.NumberFormatException: Parsing an invalid numeric string (e.g., Integer.parseInt("abc")).

The Handle-or-Declare Rule

When a method executes an expression or invokes a method that can throw a checked exception, the enclosing code must satisfy at least one of two options:

  1. Handle: Enclose the throwing call inside a try-catch block that catches the checked exception (or a superclass like Exception or Throwable).
  2. Declare: Append a throws clause to the enclosing method signature listing the checked exception (or a superclass).
import java.io.FileReader;
import java.io.IOException;

public class HandleOrDeclareDemo {
    // Option 1: Declare via throws clause
    public void readFileDeclared(String path) throws IOException {
        FileReader reader = new FileReader(path); // Constructor throws FileNotFoundException
        reader.read();
        reader.close();
    }

    // Option 2: Handle via try-catch
    public void readFileHandled(String path) {
        try {
            FileReader reader = new FileReader(path);
            reader.read();
            reader.close();
        } catch (IOException e) {
            System.err.println("I/O Error: " + e.getMessage());
        }
    }
}

[!NOTE] A method is permitted to declare checked exceptions in its throws clause even if its body never throws those exceptions. This is common when declaring abstract or interface methods intended to be overridden.


The Unreachable Catch Block Rule (Checked Exceptions)

Java enforces a strict reachability rule specifically for checked exceptions in catch blocks.

[!WARNING] If a catch block specifies a checked exception $E$, but the corresponding try block cannot possibly throw $E$ (nor any subclass of $E$), the compiler produces a compilation error: exception E is never thrown in body of corresponding try statement.

Contrast this with unchecked exceptions and general base types:

  • Catching RuntimeException, NullPointerException, IllegalArgumentException, Exception, or Throwable is always legal, even if the try block body is completely empty!
import java.io.IOException;

public class CatchReachabilityDemo {
    public void test() {
        // COMPILES: Exception and RuntimeException are always allowed
        try {
            int x = 10 / 2;
        } catch (RuntimeException e) {
            System.out.println("Caught runtime");
        } catch (Exception e) {
            System.out.println("Caught general exception");
        }

        // DOES NOT COMPILE:
        try {
            int y = 20;
        } catch (IOException e) { // ERROR: IOException is never thrown in try statement
            System.out.println("I/O failure");
        }
    }
}

Method Overriding and Checked Exceptions

When a subclass overrides a method declared in a superclass or interface, the Java compiler enforces strict covariance rules on the throws clause to preserve polymorphism:

  1. Same or Narrower Checked Exceptions: The overriding method may declare the exact same checked exceptions, narrower (subclass) checked exceptions, or a subset of them.
  2. No Broader or New Checked Exceptions: The overriding method cannot declare broader (superclass) checked exceptions or entirely new checked exceptions not declared by the superclass method.
  3. Fewer or No Checked Exceptions: The overriding method is free to declare fewer checked exceptions or omit the throws clause entirely.
  4. Unchecked Exceptions Are Free: The overriding method can declare any unchecked exceptions (RuntimeException, Error, or their subclasses), regardless of whether the superclass method declared them.
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;

class SuperClass {
    public void performAction() throws IOException {
        System.out.println("Super performing");
    }
}

class SubClassA extends SuperClass {
    @Override
    public void performAction() throws FileNotFoundException { // LEGAL: Narrower checked exception
        System.out.println("Sub A");
    }
}

class SubClassB extends SuperClass {
    @Override
    public void performAction() { // LEGAL: Declares no checked exceptions
        System.out.println("Sub B");
    }
}

class SubClassC extends SuperClass {
    @Override
    public void performAction() throws IOException, IllegalArgumentException { // LEGAL: Any unchecked exception
        System.out.println("Sub C");
    }
}

/*
class SubClassInvalid extends SuperClass {
    @Override
    public void performAction() throws Exception { // COMPILE ERROR: Broader checked exception!
    }
}

class SubClassInvalid2 extends SuperClass {
    @Override
    public void performAction() throws IOException, SQLException { // COMPILE ERROR: New checked exception SQLException!
    }
}
*/
Loading diagram...
Throwable Class Hierarchy and Compiler Rules
Test Your Knowledge

Consider a superclass method declaration: public void process() throws java.io.IOException. Which of the following method signatures in a subclass represents a legal override?

A
B
C
D
Test Your Knowledge

Given the following code snippet, what is the result of attempting to compile and execute the program?

public class InitializerDemo {
    static String[] data = new String[0];
    static String first = data[1];
    
    public static void main(String[] args) {
        System.out.println("Value: " + first);
    }
}

A
B
C
D
Test Your Knowledge

Examine the following four independent methods. Which method fails compilation due to Java's exception reachability rules?

import java.io.IOException;

class ReachabilityRules {
    void methodA() { try { int x = 10 / 2; } catch (ArithmeticException e) {} }
    void methodB() { try { int x = 10 / 2; } catch (Exception e) {} }
    void methodC() { try { int x = 10 / 2; } catch (IOException e) {} }
    void methodD() { try { int x = 10 / 2; } catch (Error e) {} }
}

A
B
C
D
Test Your Knowledge

Which of the following classes is classified as a CHECKED exception in the Java Standard Library?

A
B
C
D