Java represents thrown failures as objects belonging to the Throwable hierarchy. The two main branches are Error and Exception; RuntimeException is a branch of Exception. This structure determines which failures have compile-time handling obligations and which catch blocks can match them. It does not decide whether a particular application can recover. Learn the hierarchy together with common examples, causes, and handling boundaries so an exception name becomes useful diagnostic information rather than a label to suppress.
Throwable, Error, and Exception
Throwable is the base type accepted by throw and catch. Error commonly represents serious runtime or environment problems for which ordinary application recovery is not appropriate, such as an exhausted stack. Exception describes failures that application code more commonly anticipates. Avoid catching Throwable casually: that includes errors your operation may not be able to handle safely. Catch a meaningful exception category at a boundary where you can recover, translate, or report the failure.
Checked exceptions create a compilation obligation
A throwable type outside the RuntimeException and Error families is checked. Code that can throw a checked exception normally must catch it or declare it with throws. IOException is a common example when reading files. The compiler's obligation does not mean the failure must happen every time, and a throws declaration does not handle it. It tells callers that they must participate in the checked exception contract.
Unchecked exceptions still require judgment
RuntimeException subclasses are unchecked, so ordinary callers do not have the same catch-or-declare requirement. NumberFormatException can signal invalid text, NullPointerException can signal missing required state, and IllegalArgumentException can reject a domain input. Some are programming defects and others are expected input failures. The checked distinction is a language rule, not a universal severity ranking. Handle anticipated bad input where a useful response is available.
Common exception families at a glance
| Type | Typical meaning |
|---|---|
| IOException | A checked input/output operation failed |
| NumberFormatException | Text cannot represent the requested number |
| IndexOutOfBoundsException | A requested position is outside its valid range |
| IllegalArgumentException | An argument violates the operation contract |
| IllegalStateException | The object cannot perform the operation in its current state |
| ArithmeticException | An invalid arithmetic operation occurred |
| UnsupportedOperationException | The requested operation is not supported |
A superclass catch can match several subclasses, but broader matching also requires a broader recovery policy. Do not turn all these meanings into the same default value just because one catch can technically intercept them.
Inheritance and causes describe different relationships
An exception's superclass describes its type category. Its cause describes another failure that led to this particular failure object. An IOException can be the cause of an IllegalStateException without becoming that exception's superclass. When translating an error into a domain-specific exception, retain the cause so diagnostics can locate the original failure. A message alone may lose the stack and type information needed to distinguish an invalid file from an unrelated state problem.
Choose narrow handling and preserve context
Place a catch where you know what the failure means to that operation. A missing optional configuration file may permit a documented fallback, while a missing required export file should produce a failure response. A broad catch that reports success conceals broken work. Order subclass catches before superclass catches when using separate clauses, because an earlier broad clause would make a later narrower one unreachable. Follow exception flow to see how matching and cleanup work together.
Practice tasks and checks
Classify IOException, IllegalArgumentException, and StackOverflowError by their hierarchy branch and checked status. Then change CheckedFileFailure so creation succeeds but the read path points to an existing file, and describe the normal outcome you would add. Keep fixture cleanup separate from the read's handling contract. In ExceptionCause, inspect the cause message as well as its type and explain why copying only the message would lose information. Finally, design two different missing-file policies: an optional preferences file and a required document export. Write which boundary owns each decision. The same exception type can require different responses because the application's requirement, not the type name alone, determines recovery.
Java Summary
- Type inheritance and exception cause chains describe different relationships.
- Checked status is a compiler rule, not a severity ranking.
- Handle failures where the operation has a meaningful recovery or reporting policy.
- Continue with exception control flow when these examples are clear.
Java Examples
Each example shows the code, the output it produces and what each part does.
Inspect an actual exception ancestry
Save this program as ExceptionAncestry.java. Compile it with javac ExceptionAncestry.java and run it with java ExceptionAncestry.
Expected output:
The Class object starts at ArithmeticException. Each iteration prints the current type and moves to its superclass. The assignability condition stops before Object, which is not a Throwable type. This demonstrates that an arithmetic failure is a RuntimeException and also an Exception through inheritance. The program inspects declarations rather than deliberately performing division by zero, so no thrown failure is needed to reveal the type relationship.
public class ExceptionAncestry {
public static void main(String[] args) {
Class<?> type = ArithmeticException.class;
while (Throwable.class.isAssignableFrom(type)) {
System.out.println(type.getSimpleName());
type = type.getSuperclass();
}
}
}ArithmeticException
RuntimeException
Exception
ThrowableHandle a checked missing-file failure
Save this program as CheckedFileFailure.java. Compile it with javac CheckedFileFailure.java and run it with java CheckedFileFailure.
Expected output:
The new temporary directory contains no missing.txt file, so reading that path raises an IOException subtype. The catch provides a deterministic demonstration response without exposing a machine-specific path. Directory creation and deletion can themselves fail, which is why main still declares IOException. Catching the read does not erase the checked obligations of other file operations outside that catch. The finally block removes the empty temporary fixture.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class CheckedFileFailure {
public static void main(String[] args) throws IOException {
Path directory = Files.createTempDirectory("exception-demo-");
try {
Files.readString(directory.resolve("missing.txt"));
} catch (IOException error) {
System.out.println("Required file unavailable");
} finally {
Files.delete(directory);
}
}
}Required file unavailablePreserve the original cause when translating
Save this program as ExceptionCause.java. Compile it with javac ExceptionCause.java and run it with java ExceptionCause.
Expected output:
load constructs a contextual unchecked exception while retaining the original I/O cause. The catch prints the translated operation message and then the cause's type. These are different layers of the same failure, not two unrelated errors. A full diagnostic log could include the complete throwable chain. Preserving the cause is especially useful when a higher-level service must express a failure in its own vocabulary.
import java.io.IOException;
public class ExceptionCause {
static void load() {
IOException original = new IOException("Input interrupted");
throw new IllegalStateException("Catalog not loaded", original);
}
public static void main(String[] args) {
try {
load();
} catch (IllegalStateException error) {
System.out.println(error.getMessage());
System.out.println(error.getCause().getClass().getSimpleName());
}
}
}Catalog not loaded
IOExceptionJava FAQ
Is every Exception checked?
No. RuntimeException and its subclasses are unchecked even though they belong to Exception. The checked classification excludes that family and the Error family.
Should an application catch Error to continue?
Usually not as an ordinary recovery strategy. Error can signal conditions such as resource exhaustion where continuing normally is unsafe. A specialized boundary may report a failure, but it should not pretend the operation succeeded.
Is NullPointerException checked or unchecked?
Unchecked: it extends RuntimeException, so the compiler never forces you to catch it. It nearly always signals a bug to fix rather than a condition to handle. Since Java 14, its message explains what was null, for example Cannot invoke "String.length()" because ... is null.
Should I catch Exception or Throwable?
Avoid catching Throwable, because it also catches Errors such as OutOfMemoryError. Catching Exception is reasonable at a top-level boundary, such as a request handler or main, where you log the failure and report it. Inside ordinary methods, catch the specific types you can actually handle.
Why are checked exceptions controversial?
They make failure cases visible in method signatures, which helps callers. Critics point out that they add boilerplate, don't work smoothly with lambdas and streams, and tempt developers to catch and ignore them. Many modern frameworks therefore wrap checked exceptions in unchecked ones at their boundaries.
Java Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Which branch contains ArithmeticException?
What does a throws declaration do?
Java Quiz
2 questions to check what you learned.
What is an exception cause?
- The earlier failure leading to this failure
- Always its superclass
- Always its message string
Where should a useful catch normally be placed?
- At a boundary that understands recovery or reporting
- Around every statement blindly
- Only inside the constructor