Imagine you’re fishing with different nets. A small net (specific exception) can catch one type of fish, while a large net (general exception) catches many types. In Java, multiple catch blocks work the same way—smaller nets must come first, or the larger one will take everything, making the smaller one unreachable.
This tutorial explores multiple catch blocks, exception matching rules, and best practices in Java. You’ll learn how to handle multiple exception types properly and design robust applications.
Purpose of Exception Handling
The purpose of exception handling is to:
- Prevent unexpected application crashes.
- Separate error-handling logic from business logic.
- Allow recovery where possible.
- Ensure resources are always released.
Errors vs Exceptions in Java
At the root is Throwable
, which divides into:
Error
: Critical issues likeOutOfMemoryError
—usually unrecoverable.Exception
: Recoverable conditions that applications can handle.
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Divide by zero!");
}
Exception Hierarchy Overview
Throwable
├── Error (unrecoverable)
│ └── OutOfMemoryError, StackOverflowError
└── Exception
├── Checked (must be declared or handled)
│ └── IOException, SQLException
└── Unchecked (RuntimeException)
└── NullPointerException, ArithmeticException
Checked vs Unchecked Exceptions
- Checked exceptions: Must be declared or caught. Example:
IOException
. - Unchecked exceptions: Don’t need explicit handling. Example:
NullPointerException
.
Basic try-catch-finally
try {
FileReader fr = new FileReader("file.txt");
} catch (FileNotFoundException e) {
System.out.println("File not found!");
} finally {
System.out.println("Cleanup executed");
}
Multiple Catch Blocks
Example 1: Specific before General
try {
String text = null;
System.out.println(text.length());
} catch (NullPointerException e) {
System.out.println("Null value detected!");
} catch (RuntimeException e) {
System.out.println("General runtime issue!");
}
- Always place specific exceptions before general ones.
- Otherwise, compilation error occurs due to unreachable code.
Example 2: Using Multi-Catch (Java 7+)
try {
FileReader fr = new FileReader("data.txt");
int result = 10 / 0;
} catch (FileNotFoundException | ArithmeticException e) {
System.out.println("Either file issue or arithmetic problem: " + e);
}
- Multi-catch allows handling multiple unrelated exceptions in a single block.
- Cleaner code when handling logic is the same.
Exception Matching Rules
- Exceptions are matched from top to bottom.
- The first matching catch block is executed.
- Subclasses must come before their superclasses.
- Only one catch block runs, even if multiple could apply.
try {
int[] arr = new int[2];
arr[5] = 10;
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Array index out of bounds!");
} catch (IndexOutOfBoundsException e) {
System.out.println("General index issue");
}
Here, only the first block executes, because ArrayIndexOutOfBoundsException
is more specific.
Throwing and Declaring Exceptions
public void riskyOperation() throws IOException {
throw new IOException("File operation failed");
}
Custom Exceptions
class InvalidAgeException extends Exception {
public InvalidAgeException(String msg) { super(msg); }
}
Exception Chaining
try {
dbCall();
} catch (SQLException e) {
throw new RuntimeException("Database error", e);
}
Try-with-Resources
try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
}
Real-World Scenarios
File I/O
try {
FileReader fr = new FileReader("missing.txt");
} catch (FileNotFoundException e) {
System.out.println("File not found!");
} catch (IOException e) {
System.out.println("Other I/O issue!");
}
JDBC
try (Connection con = DriverManager.getConnection(url, user, pass)) {
// ...
} catch (SQLTimeoutException e) {
System.out.println("Database timeout!");
} catch (SQLException e) {
System.out.println("General SQL issue!");
}
REST APIs (Spring Boot)
@RestControllerAdvice
class GlobalHandler {
@ExceptionHandler({ResourceNotFoundException.class, IllegalArgumentException.class})
ResponseEntity<String> handle(Exception ex) {
return ResponseEntity.badRequest().body(ex.getMessage());
}
}
Best Practices
- Place specific catch blocks first.
- Use multi-catch for unrelated exceptions with same handling.
- Avoid catching overly broad types.
- Log exceptions with context.
- Translate low-level exceptions to domain-specific exceptions.
Anti-Patterns
- Catching
Exception
orThrowable
indiscriminately. - Empty catch blocks.
- Overlapping catch blocks making code redundant.
- Using exceptions for control flow.
Performance Considerations
- Catching exceptions itself is fast.
- Throwing exceptions is expensive.
- Avoid using exceptions for normal logic.
📌 What's New in Java Exception Handling
- Java 7+: Multi-catch and try-with-resources.
- Java 8: Exceptions in lambdas and streams.
- Java 9+: Stack-Walking API for stack trace analysis.
- Java 14+: Helpful
NullPointerException
messages. - Java 21: Structured concurrency and virtual thread exception handling.
FAQ: Expert-Level Questions
Q1. Why can’t I catch Error
?
Errors like OutOfMemoryError
are unrecoverable.
Q2. Why must specific exceptions come before general ones?
Otherwise, specific blocks become unreachable.
Q3. What’s the benefit of multi-catch?
Cleaner code when multiple exceptions share handling logic.
Q4. Does order matter in multi-catch?
No, multi-catch doesn’t allow parent-child conflicts.
Q5. Can I rethrow exceptions in multi-catch?
Yes, but the variable is implicitly final
.
Q6. Should I catch both IOException
and FileNotFoundException
?
Usually only IOException
if handling is the same, since it covers both.
Q7. What happens if no catch matches?
The exception propagates up the call stack.
Q8. Are empty catch blocks acceptable?
No—always at least log or document why ignored.
Q9. Do multiple catch blocks affect performance?
No, only thrown exceptions cost performance.
Q10. How are multiple catch rules applied in async code?
Exceptions bubble up into futures, requiring ExecutionException
handling.
Conclusion and Key Takeaways
- Multiple catch blocks let you handle different exceptions differently.
- Always order from specific to general.
- Use multi-catch for unrelated exceptions with the same handling.
- Avoid over-catching and anti-patterns.
By mastering multiple catch blocks and matching rules, you’ll design cleaner, safer, and more maintainable Java applications.