Question 25 · Section 7

Can you have multiple catch blocks for one try?

Java allows using multiple catch blocks for one try:

Language versions: English Russian Ukrainian

Junior Level

Yes, you can!

Java allows using multiple catch blocks for one try:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.FileNotFoundException;

public class FileProcessor {
    public String readFile(String path) {
        try {
            return Files.readString(Paths.get(path));
        } catch (FileNotFoundException e) {
            // Specific case - file doesn't exist
            return "default content";
        } catch (IOException e) {
            // More general - other IO errors
            throw new ServiceException("Failed to read file: " + path, e);
        }
    }
}

Rule: from specific to general

catch blocks must go from specific to general:

// Correct
try { ... }
catch (FileNotFoundException e) { }  // Specific
catch (IOException e) { }            // More general
catch (Exception e) { }              // Most general

// Incorrect - won't compile!
try { ... }
catch (Exception e) { }              // Catches everything!
catch (FileNotFoundException e) { }  // Never executes

WHY order matters: “first match wins” mechanism

The compiler checks the order of catch blocks at compile time. When you write multiple catches, the compiler builds a type hierarchy and checks that each subsequent catch is not a subtype of a previous one.

At the compiler level:

  1. Compiler analyzes each catch block top to bottom.
  2. For each catch it checks: “Is there already a catch above that is a supertype of the current one?”
  3. If yes - compilation error "exception X has already been caught", because code in the lower catch block is unreachable (unreachable code, JLS 14.21).
  4. If no - catch is added to the Exception Table.

At the JVM level (runtime):

  1. On athrow, JVM scans the Exception Table top to bottom.
  2. First match by type (or supertype) captures control.
  3. Remaining catch blocks are not checked - even if they would also match.

This is called “first match wins”. Therefore the order determines which handler gets control.

try { ... }
catch (FileNotFoundException e) { ... }  // #1: checked first
catch (IOException e) { ... }             // #2: checked only if #1 didn't match

If FileNotFoundException is thrown in try, JVM finds a match in #1 and doesn’t reach #2, even though FileNotFoundException is also an IOException.

finally after all catches

try {
    // code
} catch (IOException e) {
    // IO handler
} catch (Exception e) {
    // handler for the rest
} finally {
    // Executes after any catch
}

When NOT to use multiple catch blocks

  1. Same handling - if all catch blocks do the same thing, use multi-catch (A | B) - it’s shorter and more readable (Java 7+)
  2. More than 5 catch blocks - sign of SRP violation, method does too much. Consider splitting into several methods
  3. Catch Exception when specifics don’t matter - for generic logging, one catch is enough
  4. In functional style - instead of catch use Either/Try monads (Vavr library)
  5. When @ControllerAdvice is enough - in Spring REST one global handler replaces 10 catch blocks

Caveat: Java 7+ multi-catch as alternative

Starting from Java 7, instead of several separate catch blocks with identical logic, you can use multi-catch:

// Instead of:
catch (IOException e) {
    log.error("Error", e);
    throw new ServiceException(e);
}
catch (SQLException e) {
    log.error("Error", e);
    throw new ServiceException(e);
}

// Write:
catch (IOException | SQLException e) {
    log.error("Error", e);
    throw new ServiceException(e);
}

Multi-catch is preferable when handling is identical. Separate catches are only needed when logic differs (e.g., retry for IO, but not for SQL).


Middle Level

How JVM searches for handler

In the .class file, an Exception Table is created:

from to target type
0 10 13 FileNotFoundException
0 10 25 IOException
0 10 37 Exception

When athrow occurs, JVM scans the table top to bottom until the first match.

Anti-pattern: code duplication

// Bad - duplication
try { ... }
catch (IOException e) {
    log.error("Error", e);
    throw new ServiceException(e);
}
catch (SQLException e) {
    log.error("Error", e);       // Same!
    throw new ServiceException(e); // Same!
}

// Good - multi-catch
try { ... }
catch (IOException | SQLException e) {
    log.error("Error", e);
    throw new ServiceException(e);
}

Logic separation

If different handling is needed - separate catch blocks are justified:

try {
    // code
} catch (FileNotFoundException e) {
    log.warn("File not found, using default");
    return getDefaultData();
} catch (IOException e) {
    log.error("IO error", e);
    throw new ServiceException(e);
}

Cost of Catch

The attempt to enter a catch itself is “free” until an exception is thrown. Having an Exception Table does not slow down execution of the try block.


Senior Level

Exception Table Size

Huge number of catch blocks can bloat method size beyond 64KB -> compilation error. This is a sure sign of SRP violation - method does too much.

Polymorphism vs Catch

Instead of 10 catch blocks, sometimes better:

// Alternative - handling through hierarchy
catch (BusinessException e) {
    // Handles all subtypes: OrderException, PaymentException, etc.
    handleBusinessError(e);
}

Or Pattern Matching (Java 17+):

catch (Exception e) {
    if (e instanceof FileNotFoundException fnf) {
        // fnf is already cast to type
    } else if (e instanceof IOException io) {
        // io is already cast to type
    }
}

Catching Error

You can catch Error or Throwable, but after OutOfMemoryError the application state is unpredictable. Catch only for “last breath” - logging before crash.

Diagnostics

  • javap -v MyClass.class - shows the real Exception Table
  • Log Correlation - save common metadata (Trace ID) for all error types
  • Static Analysis - Sonar warns about duplication in catch blocks
  • Metrics - count each exception type via Micrometer

Interview Cheat Sheet

Must know:

  • Multiple catch blocks for one try - allowed and widely used
  • Rule: from specific to general - FileNotFoundException before IOException before Exception
  • Compiler checks order at compile time - wrong order = error “already been caught”
  • JVM searches handler “first match wins” - scans Exception Table top to bottom until first match
  • Having Exception Table doesn’t slow down try block execution - checking is “free” until exception
  • If handling is identical - use multi-catch (IOException | SQLException) instead of duplication
  • More than 5 catch blocks - sign of SRP violation, method does too much

Frequent follow-up questions:

  • Why can’t Exception go before IOException? - Exception catches everything, IOException becomes unreachable code
  • How does JVM search for handler? - Scans Exception Table top to bottom, first match captures control
  • When do you need separate catches instead of multi-catch? - When handling logic differs (retry for IO, not for SQL)
  • Does number of catches affect performance? - O(n) search in Exception Table, place frequent ones first

Red flags (NOT to say):

  • “I put catch (Exception e) first for simplicity” - won’t compile with more specific catches
  • “Many catch blocks don’t affect try performance” - true, but affect search on exception
  • “I use catch for Error in business logic” - after OutOfMemoryError state is unpredictable
  • “I duplicate code in each catch” - use multi-catch when handling is identical

Related topics:

  • [[25. What is multi-catch (catching multiple exceptions)]] - alternative when handling is identical
  • [[26. In what order should catch blocks be arranged]] - detailed ordering explanation
  • [[27. Can you rethrow an exception]] - rethrow from catch block
  • [[1. What is the difference between checked and unchecked exceptions]] - which exceptions to catch
  • [[4. Which exceptions must be handled]] - mandatory catches