Learning outcome: Explain the Throwable hierarchy, tell checked exception classes apart from the unchecked RuntimeException and Error families, and use Java's exception terminology correctly.
Exception: Definition and Terminology
An exception is an event that occurs during program execution and disrupts the normal flow of instructions. In Java, that event is represented by a Throwable object. When the object is thrown, the current expression, statement, or method completes abruptly. Control then moves outward through the current thread's calls until Java finds a compatible handler or the throwable remains uncaught. This definition follows Oracle's What Is an Exception? reference.
In simple terms: An exception is the program raising its hand mid-sentence to say it cannot continue as written. Java turns that moment into an object, so details of the failure can be carried back to whoever is able to deal with it.
The thrown object records its class and may also contain a detail message, a cause, and a stack trace. The class determines which catch clauses can handle it.
The terminology has two common uses. The Java Language Specification (JLS), Section 11.1.1, uses exception classes for Throwable and all its subclasses, including the Exception and Error branches. In narrower application terminology, exception often refers only to the Exception branch.
Error is also a specific class name. A missing semicolon produces a compilation error, but it does not create or throw a java.lang.Error object. java.lang.Error belongs to the runtime Throwable hierarchy.
Terminology note: The phrase “compile-time exception” is misleading. A checked exception is not thrown while the compiler examines source code. Checked means that the compiler requires the possible exception to be caught or declared. Failure to satisfy that rule produces a compilation error. The JLS calls the rule compile-time checking of exceptions.
Throwing and Propagation
Throwing and propagation follow this sequence:
- The JVM detects a condition such as integer division by zero, or application code reaches a
throwstatement. - A
Throwableobject is thrown. It may have just been created, or it may be an object that already existed. - The runtime searches outward through the current thread's call stack for a compatible
catchclause. - If no compatible handler is found, relevant
finallyblocks run, an uncaught-exception handler is invoked, and the current thread terminates.
An uncaught throwable terminates the current thread, not necessarily the entire JVM. A simple program usually ends after an uncaught throwable in main because no other non-daemon thread remains. The complete rules are specified in JLS Section 11.3, Run-Time Handling of an Exception.
public class DivideDemo {
static int divide(int a, int b) {
return a / b;
}
public static void main(String[] args) {
System.out.println(divide(10, 0));
}
}
The example compiles without catch or throws because ArithmeticException is unchecked. The JVM throws the exception when divide(10, 0) executes. Since no matching handler is present, the main thread terminates.
The Throwable Hierarchy
Solid lines show direct inheritance. JLS Section 11.1.1 defines the complete compiler classification:
Errorand all its subclasses are unchecked.RuntimeExceptionand all its subclasses are unchecked.- Under the exact JLS definition, every other class in the
Throwablehierarchy is checked.
Exception has many direct and indirect subclasses. Custom checked exceptions normally extend Exception, but not RuntimeException. Directly extending Throwable is legal but uncommon.
Error, Checked Exception, and RuntimeException
The three principal groups differ as follows:
| Family | Compiler rule | Examples | Typical response |
|---|---|---|---|
Error and its subclasses |
Unchecked | OutOfMemoryError, StackOverflowError, LinkageError |
Let it propagate and investigate the cause; recovery is not normally attempted. |
RuntimeException and its subclasses |
Unchecked | NullPointerException, IllegalArgumentException, ArithmeticException |
Usually invalid input, a failed precondition, or a defect. Recovery may still fit at a boundary. |
Other Exception subclasses |
Checked — catch or declare | IOException, SQLException, InterruptedException |
Handle where a useful response exists, or add context and rethrow with throws. |
In simple terms: An Error reports a failure in the JVM itself, and an application is not expected to recover from it. A checked exception reports an external condition that the compiler requires the program to address. A RuntimeException usually reports a defect in the program’s own logic.
The terms checked and unchecked describe only what the compiler requires. They do not indicate how serious a failure is, or whether recovery is possible. An unchecked NumberFormatException caused by invalid input is often easy to handle. A checked IOException may have no useful remedy at the point where it is thrown.
RuntimeException is the name of a single class. It is not a general term for every exception that occurs while a program runs. Checked exceptions are also thrown at run time. The difference is that the compiler confirms in advance that they are caught or declared.
Java allows an Error to be caught, but application code should rarely do so. A framework or a test may catch one specific Error for a particular purpose, such as reporting it. That is not the same as recovering from it. The JLS leaves the error classes unchecked because they can arise at almost any point in a program, and recovery is usually impractical.
Compile-Time Checking and Runtime Throwing
This example violates the catch-or-declare rule:
import java.io.IOException;
class CheckedRuleDemo {
static void loadData() throws IOException {
throw new IOException("data unavailable");
}
static void start() {
loadData(); // compile-time error: IOException is neither caught nor declared
}
}
The compiler does not throw an IOException. It determines that loadData() can throw one and rejects start() because the method neither catches nor declares that possibility. Adding a suitable catch clause or declaring start() throws IOException satisfies the compiler rule. An IOException can be thrown only later, if the compiled program executes the relevant code.
The catch-or-declare rule does not apply to ArithmeticException or NullPointerException. These exceptions are still thrown at runtime and may still be caught. Unchecked means that the compiler does not require handling or declaration; it does not mean undetectable or uncatchable.
Catch-Type Matching
A catch clause handles objects of its declared parameter type and objects of any subclass of that type.
In simple terms: A catch for a parent type also catches every child type. Catching Exception therefore catches almost everything, which is convenient and almost always too coarse to be useful.
| Handler | What it catches |
|---|---|
catch (RuntimeException e) |
RuntimeException and its subclasses only |
catch (Exception e) |
The whole Exception branch: its checked subclasses and the RuntimeException family, but not the Error branch |
catch (Error e) |
Error and its subclasses only; rarely appropriate as a general recovery policy |
catch (Throwable t) |
Every throwable, including both the Exception and Error branches; almost always too broad for application recovery |
When several handlers are present, the more specific handler must appear first. A preceding broader handler would already match the narrower type and would make the later handler unreachable.
Useful Throwable Methods
| Method | Purpose |
|---|---|
getMessage() |
Returns the detail message, which may be null. |
toString() |
Returns the class name followed by the message, when one is present. |
printStackTrace() |
Prints the throwable and its call sequence to standard error by default. |
getCause() |
Returns the underlying cause when the throwable was created with one. |
try {
Integer.parseInt("ten");
} catch (NumberFormatException exception) {
System.out.println(exception.getClass().getSimpleName());
System.out.println(exception.getMessage());
}
Reading a Stack Trace
A stack trace identifies the throwable type, its message, and the chain of method calls that led to it. Analysis begins with the type and message, followed by the first frame that belongs to the application. If the trace contains one or more Caused by: sections, the deepest relevant cause should also be examined.
In simple terms: Read the top line for what went wrong and the list beneath it for how the program got there. The first line naming one of your own classes is almost always where to start looking.
java.lang.ArithmeticException: / by zero
at Calculator.divide(Calculator.java:4)
at Calculator.main(Calculator.java:8)
In this trace, Calculator.divide is the first application frame and therefore the appropriate starting point for investigating the division by zero.
From JDK 15 onward, a NullPointerException also names the expression that was null. A line holding several dereferences no longer has to be narrowed down by trial.
Exception in thread "main" java.lang.NullPointerException: Cannot read field "name" because "order.customer" is null
at Billing.print(Billing.java:6)
The message identifies order.customer rather than only line 6, so the null reference is located without adding temporary print statements.
Names appear in that message only when the class file retains them. Compiling with javac -g, as development environments normally do, produces "order.customer"; compiling without it produces a placeholder such as "<parameter1>.customer". The rest of the message is unchanged, and the reasoning is the same.
Common Mistakes
| Mistake | Correction |
|---|---|
| Calling a checked exception a “compile-time exception” | Say checked exception. The compiler checks the handling rule; the exception itself can be thrown only when the relevant code runs. |
Calling every programming problem an Error |
Use Error for the java.lang.Error hierarchy. Use compilation error, exception, or logic defect for the other cases. |
Assuming every unchecked throwable is a RuntimeException |
Remember that the complete Error branch is unchecked too. |
| Treating checked as recoverable and unchecked as unrecoverable | The classification tells you what the compiler requires, not whether recovery will succeed. |
Routinely catching Throwable or Error |
Catch the narrowest exception type the program can handle meaningfully. |
| Ignoring the cause chain | Read the frames from your application and every relevant Caused by: section. |
| Using exceptions for normal, expected choices | Test expected conditions directly when that makes the control flow clearer. |
Practice
- Trigger and inspect an
ArithmeticException. - Trigger a
NumberFormatExceptionby parsing invalid numeric text. - Classify
IOException,NullPointerException, andStackOverflowErrorby hierarchy branch and compiler rule. - Explain why an unchecked exception may still be recoverable at an input boundary.
- Explain why
catch (Exception e)does not catchOutOfMemoryError. - Explain why “compile-time exception” is misleading terminology.
Quick Check
1. What is an exception in Java?
An exception is an event that occurs during program execution and disrupts the normal flow of instructions. Java represents the event with a Throwable object; when that object is thrown, control moves outward in search of a compatible handler.
2. What is the root class of Java's runtime throwable hierarchy?
java.lang.Throwable is the root. Its two main branches are Error and Exception.
3. Which two families are unchecked by the compiler?
The complete RuntimeException and Error families are unchecked.
4. Is a compiler error a java.lang.Error?
No. A compiler error is a diagnostic that prevents successful compilation. A java.lang.Error is an object that can be thrown while a program is running.
5. What happens when no compatible handler is found?
The throwable moves outward through the current thread's calls, and relevant finally blocks run. If it is still unhandled, Java invokes an uncaught-exception handler and ends that thread. The default handler normally prints the throwable and its stack trace.
6. Does unchecked mean the condition cannot be caught?
No. It means the compiler does not require catch-or-declare handling. The program can still catch it, although doing so is useful only when a meaningful response is possible.
7. Why should checked exceptions not be called “compile-time exceptions”?
The compiler checks whether the program catches or declares them, but it does not throw them during compilation. If the program fails that check, the result is a compilation error. An exception object is thrown only when the relevant code executes.
Summary
An exception is an event that occurs during program execution and disrupts the normal flow of instructions. Java represents the event with a thrown Throwable object. Throwable divides principally into Error and Exception, with RuntimeException inside the Exception branch. The complete Error and RuntimeException families are unchecked; under the JLS definition, the remaining Throwable classes are checked. These classifications state what the compiler requires, not when the event occurs or whether recovery is possible. Catch-or-declare checking occurs during compilation, whereas exception objects are thrown and caught during program execution.