Question 26 · Section 7

What is multi-catch (catching multiple exceptions)?

Before Java 7, developers faced two problems:

Language versions: English Russian Ukrainian

Junior Level

Definition

Multi-catch - a Java 7+ feature allowing you to catch multiple exception types in a single catch block.

Why multi-catch appeared specifically in Java 7

Before Java 7, developers faced two problems:

  1. Code duplication: if IOException and SQLException are handled identically, you had to write two identical catch blocks.
  2. Common ancestor is too broad: catching Exception is too coarse, you lose typing on propagation (throws Exception).

In Java 7, the Coin project (Small Language Changes) added multi-catch as a solution to these problems. Alongside appeared try-with-resources and Precise Rethrow - all three features worked together to improve exception handling.

import java.io.IOException;
import java.sql.SQLException;

public class DataService {
    public void loadData() {
        try {
            // IO + DB operations
        } catch (IOException | SQLException e) {
            // One block for both exceptions
            // e.getClass() returns the real type: IOException or SQLException
            throw new DataException("Resource operation failed", e);
        }
    }
}

Syntax

Types are separated by vertical bar |:

catch (ExceptionType1 | ExceptionType2 | ExceptionType3 e) {
    // Common handling
}

Limitation: types in multi-catch must not be in inheritance relation

The compiler forbids specifying types related by inheritance:

// Compilation error - FileNotFoundException inherits IOException
catch (FileNotFoundException | IOException e) { }

WHY this limitation: if the compiler allowed FileNotFoundException | IOException, there would be ambiguity. FileNotFoundException is already an IOException, so multi-catch with both types is redundant - IOException covers both cases. The compiler considers this an error to prevent confusion.

How compiler checks: when compiling multi-catch, the compiler builds pairwise check: for each pair of types (A, B) in multi-catch it checks that A is not a subtype of B and B is not a subtype of A. If violated - error "Alternatives in a multi-catch statement cannot be related by subclassing".

How to fix: remove the subtype from multi-catch:

// Correct - only parent covers all cases
catch (IOException e) { }

// Or separate catches if different handling is needed:
catch (FileNotFoundException e) { /* specific */ }
catch (IOException e) { /* rest */ }

Exception variable

In multi-catch the variable e is implicitly final - cannot reassign:

catch (IOException | SQLException e) {
    e = new Exception(); // Compilation error!
}

Middle Level

Precise Rethrow

Critically important feature of Java 7+:

public void process() throws IOException, SQLException { // Exact types!
    try {
        // code that throws IOException or SQLException
    } catch (Exception e) { // Catch common ancestor
        log.error("Error occurred");
        throw e; // Compiler understands only IOException or SQLException will fly
    }
}

Before Java 7, you’d have to declare throws Exception. Now the compiler analyzes the contents of try and allows propagation without losing typing.

Bytecode

Multi-catch doesn’t create one common handler. In the Exception Table, separate entries are created for each type, pointing to the same target:

from to target type
0 10 15 IOException
0 10 15 SQLException

Advantages

  • Less code - no duplicated handling
  • Smaller .class file - bytecode is not duplicated
  • Cleaner signature - Precise Rethrow preserves exact types

Disadvantages

  • Variable e is effectively final
  • Cannot separate logic for different types (use separate catch for that)

When NOT to use multi-catch

  1. Different handling - if IOException requires retry but SQLException doesn’t, separate catches. Multi-catch assumes identical logic for all types.
  2. Different HTTP statuses - ValidationException -> 400, DataAccessException -> 500. Different statuses = different handling = separate catches.
  3. Interrupted + IOException - InterruptedException requires restoring interrupt flag (Thread.currentThread().interrupt()), normal IO doesn’t. Combining leads to flag loss.
  4. More than 3 types - catch (A | B | C | D e) becomes unreadable. If you need 4+ types, the method likely does too much.
  5. When you need to add different context - "File not found" vs "DB unavailable" require different error messages. In multi-catch the message will be general and less informative.
  6. When one of the types is RuntimeException - unchecked exceptions are usually not caught explicitly. Their presence in multi-catch signals a design worth reconsidering.

Senior Level

Reflection and multi-catch

Clean code when handling reflection exceptions:

try {
    Method method = MyClass.class.getDeclaredMethod("doSomething");
    method.invoke(obj);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
    log.error("Reflection failed", e);
    throw new FrameworkException("Method invocation failed", e);
}

InterruptedException in multi-catch

If combining InterruptedException with others, don’t forget to restore the interrupt status:

catch (InterruptedException | IOException e) {
    if (e instanceof InterruptedException) {
        Thread.currentThread().interrupt();
    }
    log.error("Interrupted or IO error", e);
}

Code Size and Maintenance

Multi-catch reduces .class file size and simplifies maintenance in high-load systems where error logging is standardized.

Precise Logging

e.getClass().getName() returns the name of the actual thrown class, not the common ancestor:

catch (IOException | SQLException e) {
    log.error("{} occurred", e.getClass().getSimpleName(), e);
    // Will print "FileNotFoundException" or "SQLTimeoutException"
}

Diagnostics

  • SonarLint - suggests replacing identical catch blocks with multi-catch
  • javap -v - shows separate Exception Table entries for each type
  • Metrics - e.getClass().getSimpleName() for categorization in Prometheus

Interview Cheat Sheet

Must know:

  • Multi-catch (Java 7+) - catching multiple exception types in one catch via |
  • Appeared in Coin project (Java 7) together with try-with-resources and Precise Rethrow
  • Types in multi-catch must not be in inheritance relation - FileNotFoundException | IOException = compilation error
  • Variable e in multi-catch is implicitly final - cannot reassign
  • In Exception Table, separate entries are created for each type, pointing to one target
  • Precise Rethrow: catch (Exception e) { throw e; } preserves exact types in throws (Java 7+)

Frequent follow-up questions:

  • Why can’t FileNotFoundException | IOException? - FileNotFoundException is already an IOException, ambiguity
  • What is Precise Rethrow? - Compiler analyzes try and understands exact types on throw e from catch (Exception e)
  • When NOT to use multi-catch? - Different handling, different HTTP statuses, InterruptedException (requires flag restoration)
  • How to know the real type in multi-catch? - e.getClass() returns the actual type, not the common ancestor

Red flags (NOT to say):

  • “I combine InterruptedException with others without restoring flag” - thread interrupt lost
  • “Multi-catch creates one common handler in bytecode” - no, separate entries for each type
  • “I write throws Exception with multi-catch” - Precise Rethrow allows exact types
  • “Multi-catch is slower than separate catches” - same, same Exception Table

Related topics:

  • [[24. Can you have multiple catch blocks for one try]] - separate catches for different handling
  • [[26. In what order should catch blocks be arranged]] - ordering doesn’t matter inside multi-catch
  • [[9. What is try-with-resources]] - appeared in the same Java 7
  • [[20. What does the throws keyword do]] - Precise Rethrow preserves exact types
  • [[3. What is an unchecked exception (Runtime Exception)]] - RuntimeException not recommended for multi-catch