What is multi-catch (catching multiple exceptions)?
Before Java 7, developers faced two problems:
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:
- Code duplication: if
IOExceptionandSQLExceptionare handled identically, you had to write two identical catch blocks. - Common ancestor is too broad: catching
Exceptionis 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
eiseffectively final - Cannot separate logic for different types (use separate
catchfor that)
When NOT to use multi-catch
- Different handling - if
IOExceptionrequires retry butSQLExceptiondoesn’t, separate catches. Multi-catch assumes identical logic for all types. - Different HTTP statuses -
ValidationException-> 400,DataAccessException-> 500. Different statuses = different handling = separate catches. - Interrupted + IOException -
InterruptedExceptionrequires restoring interrupt flag (Thread.currentThread().interrupt()), normal IO doesn’t. Combining leads to flag loss. - More than 3 types -
catch (A | B | C | D e)becomes unreadable. If you need 4+ types, the method likely does too much. - 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. - 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
catchblocks 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
catchvia| - 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
ein 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 inthrows(Java 7+)
Frequent follow-up questions:
- Why can’t
FileNotFoundException | IOException? -FileNotFoundExceptionis already anIOException, ambiguity - What is Precise Rethrow? - Compiler analyzes
tryand understands exact types onthrow efromcatch (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
InterruptedExceptionwith others without restoring flag” - thread interrupt lost - “Multi-catch creates one common handler in bytecode” - no, separate entries for each type
- “I write
throws Exceptionwith 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