10.1 The Java Exception Hierarchy: Checked vs. Unchecked Exceptions and Errors
Key Takeaways
java.lang.Throwable is the root superclass of all errors and exceptions in the Java platform, bifurcating into java.lang.Error and java.lang.Exception.
Errors (such as OutOfMemoryError, StackOverflowError, and AssertionError) represent fatal system conditions or JVM failures that standard application code must never attempt to catch or recover from.
Checked exceptions are subclasses of Exception (excluding RuntimeException); the compiler enforces the 'catch-or-declare' rule, requiring them to be either caught in a try-catch block or declared in a method's throws clause.
Unchecked exceptions are subclasses of RuntimeException (or Error) that represent programming bugs or invalid states (such as NullPointerException and IllegalArgumentException); they are exempt from compiler catch-or-declare enforcement.
The compiler enforces strict reachability rules on checked exceptions: attempting to catch a checked exception that cannot be thrown within the enclosed try block results in a compile-time error, whereas catching unchecked exceptions, Exception, or Throwable is always permitted.
10.1 The Java Exception Hierarchy: Checked vs. Unchecked Exceptions and Errors
In Java, exceptional conditions that disrupt the normal flow of program execution are represented as first-class objects. Robust exception handling requires a deep understanding of the object model rooted at java.lang.Throwable, the fundamental architectural boundary separating recoverable conditions from catastrophic system failures, and the compiler's strict reachability rules.
The Throwable Class Hierarchy
Every exception and error in Java is an instance of java.lang.Throwable or one of its direct or indirect subclasses. The hierarchy splits into two primary branches:
java.lang.Throwable
|
+-----------------+-----------------+
| |
java.lang.Error java.lang.Exception
| |
+----------+----------+ +----------+----------+
| | | |
OutOfMemoryError StackOverflowError | RuntimeException
| |
(Checked Exceptions) +------+------+
e.g. IOException | |
e.g. SQLException NullPointer- Arithmetic-
Exception Exception
java.lang.Throwable: The ultimate root of the exception hierarchy. Only instances of this class (or its subclasses) can be thrown by the Java Virtual Machine (JVM) or by thethrowstatement, and only these types can appear in acatchclause orthrowsdeclaration.java.lang.Error: Represents serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions that occur within the virtual machine itself or the underlying hardware.java.lang.Exception: Represents conditions that a reasonable application might want to catch and recover from.java.lang.RuntimeException: A special branch ofException. Subclasses ofRuntimeException(along withError) are collectively termed unchecked exceptions. All other subclasses ofExceptionare termed checked exceptions.
The Error Category: Unrecoverable JVM Failures
Instances of java.lang.Error designate fatal infrastructure issues or system resource exhaustion. When an Error occurs, the integrity of the JVM or the operating environment is typically compromised.
Key examples to recognize:
OutOfMemoryError: Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector.StackOverflowError: Thrown when a thread stack overflows, most frequently due to infinite recursion or excessively deep call stacks.NoClassDefFoundError: Thrown when the JVM or a ClassLoader instance tries to load in the definition of a class (as part of a normal method call or creation of a new instance) and no definition of the class could be found at runtime, even though it compiled cleanly.ExceptionInInitializerError: Signals that an unexpected exception occurred in a static initializer block or during the evaluation of a static field initializer.AssertionError: Thrown to indicate that anassertstatement has failed.VirtualMachineError: The abstract superclass ofOutOfMemoryErrorandStackOverflowError.
Warning
Catching Error Is an Anti-Pattern
While writing catch (Error e) or catch (Throwable t) is syntactically legal, application code should never attempt to handle or suppress Error subclasses (such as OutOfMemoryError or StackOverflowError). Attempting to continue execution after a JVM failure leaves threads in undefined, corrupted states. Let errors propagate to terminate the failing thread or process.
Checked vs. Unchecked Exceptions
The distinction between checked and unchecked exceptions is enforced entirely by the Java compiler.
The Catch-or-Declare Rule
Java enforces the catch-or-declare contract for all checked exceptions:
- Catch: Enclose the code that may throw the exception in a
tryblock with an associatedcatchblock that handles the exception type (or a supertype). - Declare: Append a
throwsclause to the enclosing method or constructor signature declaring that the method may propagate the exception to its caller.
If a method contains an invocation of code that throws a checked exception without either catching it or declaring it in its throws clause, the Java compiler refuses to compile the code.
import java.io.*;
public class CatchOrDeclareDemo {
// Approach 1: Declare via throws clause
public void readFile(String path) throws IOException {
FileReader reader = new FileReader(path); // FileReader throws FileNotFoundException (checked)
reader.read(); // read() throws IOException (checked)
reader.close();
}
// Approach 2: Catch within try-catch block
public void safeReadFile(String path) {
try {
FileReader reader = new FileReader(path);
reader.read();
reader.close();
} catch (IOException e) {
System.err.println("Failed to read file: " + e.getMessage());
}
}
}
Unchecked Exceptions (RuntimeException)
Unchecked exceptions are subclasses of java.lang.RuntimeException. They represent flaws in program logic, illegal arguments, or violated object states that can theoretically occur anywhere in an application.
Because runtime errors can happen in virtually any statement (such as dereferencing a reference or dividing numbers), forcing developers to declare them everywhere would clutter code with boilerplate. Therefore, unchecked exceptions are exempt from the catch-or-declare rule.
| Dimension | Checked Exceptions | Unchecked Exceptions (RuntimeException) | System Errors (Error) |
|---|---|---|---|
| Superclass | Subclasses of Exception outside the RuntimeException branch | Subclasses of java.lang.RuntimeException | Subclasses of java.lang.Error |
| Catch-or-Declare Rule | Mandatory: Compiler forces try-catch or throws | Optional: Compiler does not require catch or declare | Optional: Must not be caught by normal logic |
| Primary Root Cause | External conditions (missing files, network dropped, SQL errors) | Programming bugs, invalid input, state contract violations | JVM resource exhaustion, linkage/classloading failures |
| Recovery Feasibility | Generally recoverable with alternative workflows | Recoverable through bug fixes and defensive preconditions | Unrecoverable; system/JVM state is damaged |
Common Built-in Exceptions for 1Z0-829
Exam questions frequently require identifying whether a specific exception is checked or unchecked, and what runtime scenario causes it to be thrown.
Unchecked Runtime Exceptions (java.lang)
| Exception | Common Trigger Scenario |
|---|---|
NullPointerException | Attempting to invoke an instance method, access an instance field, or calculate the length of an array through a null reference, or unboxing a null wrapper object. |
ArrayIndexOutOfBoundsException | Accessing an array with an illegal index that is either negative or greater than or equal to array.length. |
StringIndexOutOfBoundsException | Accessing a character of a String with an index that is out of bounds (subclass of IndexOutOfBoundsException). |
ClassCastException | Attempting to cast an object to a class of which it is not an instance (e.g., (String) (Object) Integer.valueOf(5)). |
IllegalArgumentException | A method received an argument that is inappropriate, invalid, or violates method preconditions. |
IllegalStateException | A method has been invoked at an illegal or inappropriate time (the object environment or lifecycle state is not suitable for the requested operation, e.g. calling iterator.remove() before next()). |
NumberFormatException | Attempting to convert a string to a numeric type when the string does not have an appropriate format (subclass of IllegalArgumentException). |
ArithmeticException | Exceptional arithmetic condition, specifically integer division or modulo by zero (10 / 0). |
UnsupportedOperationException | Invoking a mutating method on an unmodifiable collection (e.g., List.of().add("x")). |
Warning
The Floating-Point Division by Zero Trap
In Java, integer division by zero (10 / 0) throws java.lang.ArithmeticException. However, floating-point division by zero (10.0 / 0.0 or 10.0f / 0) does not throw an exception! In compliance with IEEE 754 floating-point standards, it evaluates to Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, or Double.NaN (for 0.0 / 0.0). Watch for floating-point literals in arithmetic questions!
Checked Exceptions
| Exception | Package | Common Trigger Scenario |
|---|---|---|
IOException | java.io | Failed or interrupted I/O operations (broken network connection, inaccessible pipe). |
FileNotFoundException | java.io | Attempting to open a file denoted by a specified pathname when the file does not exist (subclass of IOException). |
SQLException | java.sql | Database access errors, invalid SQL syntax, or lost database connections. |
ClassNotFoundException | java.lang | An application tries to load a class through its string name (e.g., Class.forName("com.Foo")) but no definition can be located. |
InterruptedException | java.lang | A thread is waiting, sleeping, or occupied, and another thread interrupts it using Thread.interrupt(). |
ParseException | java.text | An unexpected error has been reached while parsing a date or number string. |
Compiler Reachability and Catch Block Validity
One of the trickiest areas on the 1Z0-829 exam involves compiler reachability checks on catch blocks. The Java compiler verifies whether a try block could ever possibly throw the checked exception listed in a catch clause:
- Unreachable Checked Exception Catch Error: If a
catchblock catches a checked exception , and the statements within the correspondingtryblock cannot possibly throw (or any subclass of ), the compiler rejects the code with an error:exception E is never thrown in body of corresponding try statement. - The Unchecked and Broad Catch Exemption: This reachability check does not apply to unchecked exceptions (
RuntimeExceptionor any of its subclasses) or to broad catch types (ExceptionandThrowable). You can catchNullPointerException,RuntimeException,Exception, orThrowablearound any arbitrary code, even an emptytryblock!
import java.io.IOException;
public class ReachabilityRules {
public void testReachability() {
try {
System.out.println("Processing...");
} catch (RuntimeException e) {
// COMPILES: Unchecked exceptions are always permitted in catch blocks
} catch (Exception e) {
// COMPILES: Exception is broad and could represent runtime exceptions
}
try {
System.out.println("Testing checked...");
// catch (IOException e) {} // COMPILE ERROR: IOException is never thrown in try block!
} catch (Exception e) {
// Valid
}
}
}
Java Assertions: Invariant Verification (Background)
Note
Assertions are not named in the current 1Z0-829 objectives. They appear briefly here because AssertionError belongs to the Error hierarchy and older study material still uses them; spend most of your time on the exception rules above.
Assertions, introduced via the assert keyword, enable developers to test internal assumptions and invariants during development and testing without incurring performance overhead in production.
Assertion Syntax
Java provides two forms of the assert statement:
- Simple assertion:
assert booleanExpression; - Assertion with detail expression:
assert booleanExpression : detailExpression;
If assertions are enabled and booleanExpression evaluates to false, the JVM throws an AssertionError. In the two-argument form, detailExpression is evaluated and converted to a String to serve as the detail message of the AssertionError.
Controlling Assertions via the Command Line
By default, assertions are disabled at runtime. They must be explicitly activated using CLI flags:
-eaor-enableassertions: Enables assertions for all system-loaded application classes.-daor-disableassertions: Disables assertions.- Granular control:
-ea:com.order...: Enables assertions for packagecom.orderand all its subpackages (the trailing...denotes subpackages).-da:com.order.Pricing: Disables assertions for the specific classPricingwithincom.order.- More specific rules take precedence over broader rules.
# Enable in all packages except com.example.cache
java -ea -da:com.example.cache... com.example.MainApp
Warning
Assertion Rules and Anti-Patterns
- Do not use assertions to validate arguments in public methods: Public methods must throw standard exceptions like
IllegalArgumentExceptionorNullPointerExceptionbecause assertions can be disabled in production, leaving public API contracts unprotected. Use assertions for private method preconditions, postconditions, and internal invariant validation. - Never write assertions with side effects: Expressions such as
assert list.remove(item);orassert count++ > 0;alter program state when-eais active, but fail to execute when assertions are disabled, causing divergent behavior between test and production environments!
Consider the following code snippet:
public class ExceptionReachability {
public static void main(String[] args) {
try {
System.out.println("Parsing number...");
int val = Integer.parseInt("123");
} catch (java.io.IOException e) {
System.out.println("IO Error");
} catch (NumberFormatException e) {
System.out.println("Format Error");
}
}
}
What is the result of attempting to compile and run this code?
Compilation fails because NumberFormatException is an unchecked exception and cannot be caught.
Compiles successfully and prints: Parsing number...
Compilation fails because java.io.IOException is never thrown inside the try block.
Compiles successfully, but throws an UncheckedIOException at runtime.
Which of the following classes is a checked exception that requires either handling via a try-catch block or declaration in a method's throws clause?
java.lang.ClassNotFoundException
java.lang.AssertionError
java.lang.IllegalStateException
java.lang.IllegalArgumentException
What does the following code print?
try {
int n = Integer.parseInt("12a");
System.out.print("A ");
} catch (IllegalArgumentException e) {
System.out.print("B ");
} catch (RuntimeException e) {
System.out.print("C ");
} finally {
System.out.print("D");
}
C D
A D
B D
Compilation fails because NumberFormatException is not caught by name
What is the result of executing the following two statements in Java?
double d = 10.0 / 0.0;
int i = 10 / 0;
Both statements cause a compile-time error because division by zero is statically forbidden.
Both statements throw an ArithmeticException at runtime.
The first statement throws an ArithmeticException; the second assigns 0 to i.
The first statement assigns Infinity to d without throwing an exception; the second statement throws an ArithmeticException at runtime.
Sections you finish are checked off in the contents.