Mastering Multiple Catch Blocks and Exception Matching Rules in Java

Illustration for Mastering Multiple Catch Blocks and Exception Matching Rules in Java
By Last updated:

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 like OutOfMemoryError—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

  1. Exceptions are matched from top to bottom.
  2. The first matching catch block is executed.
  3. Subclasses must come before their superclasses.
  4. 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 or Throwable 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.