In what order should catch blocks be arranged?
catch blocks must go from more specific to more general:
Junior Level
Main rule: from specific to general
catch blocks must go from more specific to more general:
try {
Files.readAllLines(Paths.get("file.txt"));
} catch (FileNotFoundException e) {
// 1. Specific case first
System.out.println("File not found");
} catch (IOException e) {
// 2. More general next
System.out.println("IO error");
} catch (Exception e) {
// 3. Most general - at the end
System.out.println("Unknown error");
}
Why
If you put the general catch first, it catches everything, and specific catch blocks never execute:
// Compilation error!
try {
// code
} catch (Exception e) {
// Catches everything, including IOException
} catch (IOException e) {
// Never executes - code is unreachable
}
Compiler outputs: "exception IOException has already been caught".
“First match wins” mechanism at the compiler level
At compile time:
The javac compiler checks each catch block sequentially top to bottom (JLS 14.20). For each new catch it asks: “Is this catch’s type a subtype of any catch that was above?”
- If yes - compiler issues an error, because code in this catch block is unreachable (unreachable code, JLS 14.21).
- If no - catch is added to the class’s Exception Table.
This is a static check - compiler doesn’t allow compilation with wrong order. Therefore in properly compiled code the order is always correct.
At runtime (JVM):
Exception Table is an array of {from, to, target, type} entries. When an exception is thrown, JVM:
- Searches for the first entry where the type matches the exception (or its supertype).
- Jumps to
target- address of the corresponding catch block. - Remaining entries are ignored.
Therefore “first match” is literally the first matching entry in the Exception Table.
Simple rule
Arrange by inheritance hierarchy - from children to parents.
When NOT to follow the “specific to general” rule
- Never violate it - this rule is enforced by the compiler, reverse order won’t compile
- Multi-catch inside - order of types in
catch (A | B e)doesn’t matter, because both types point to the same target in Exception Table - Don’t catch
Throwablein business logic - only at the system boundary - Don’t put rare exceptions before frequent ones - if
FileNotFoundExceptionoccurs 100x more often thanEOFException, catch it first for micro-optimization of Exception Table search - Don’t mix abstraction levels - don’t alternate business and infrastructure exceptions without logic
Caveat: Java 7+ multi-catch changes the approach to ordering
With the appearance of multi-catch in Java 7, the ordering question becomes less critical in some scenarios. If you have 3+ exceptions with identical handling, instead of:
// 3 catch blocks - order matters (FileNotFoundException before IOException)
catch (FileNotFoundException e) { handle(); }
catch (SQLException e) { handle(); }
catch (IOException e) { handle(); }
You can write:
// 1 catch block - order inside multi-catch does NOT matter
catch (FileNotFoundException | SQLException | IOException e) { handle(); }
Multi-catch removes the ordering problem, because all types are handled identically and point to one target in the Exception Table.
When catch block order does NOT matter
-
All catch blocks handle identically - if each catch does
log.error(e); throw new ServiceException(e);, order doesn’t affect the result. However in this case it’s better to use multi-catch. -
Exceptions are not related by inheritance -
catch (SQLException e)andcatch (NullPointerException e)have no common subtype. They never intersect, and order between them doesn’t affect correctness (though it may affect search performance). -
Multi-catch - inside
catch (A | B e)the order of types doesn’t matter. Compiler generates separate entries in Exception Table, but they all point to one target. -
Single catch block -
catch (Exception e)- nothing to order.
Middle Level
First Match Wins
JVM scans Exception Table top to bottom. First match captures control.
Arrangement strategy
1. Expected vs Exceptional:
Catch the most frequent business exceptions (e.g., ValidationException) first - they’ll be at the start of the Exception Table.
2. Different handling:
@PostMapping("/orders")
public ResponseEntity<?> createOrder(@RequestBody OrderRequest req) {
try {
return ResponseEntity.ok(orderService.create(req));
} catch (ValidationException e) {
// 400 Bad Request - client error
return ResponseEntity.badRequest().body(e.getMessage());
} catch (ResourceNotFoundException e) {
// 404 Not Found
return ResponseEntity.notFound().build();
} catch (DataAccessException e) {
// 500 Server Error - our problem
log.error("Database error processing order", e);
return ResponseEntity.internalServerError().build();
}
}
Multi-catch order
In catch (A | B e) the order within the block doesn’t matter:
catch (IOException | SQLException e) { }
catch (SQLException | IOException e) { }
// Same thing
But you still can’t combine parent and child.
Interaction with try-with-resources
Exceptions on automatic resource closing (close()) occur before reaching your catch blocks. Your catch may catch an exception from close() if there were no errors in the main try.
Senior Level
Exception Table and performance
Search in Exception Table - complexity O(n). In methods with dozens of catch blocks this can have a microscopic impact on performance. Place the most frequent exceptions first.
Handling Error
If you catch Throwable, you also catch Error (e.g., OutOfMemoryError):
// Rule: specific Exception above Throwable
try { ... }
catch (BusinessException e) { } // Business errors
catch (DataAccessException e) { } // Infrastructure
catch (Throwable t) { } // Only at the end, at system boundary
Never do catch (Throwable t) in the middle of business logic - only at the outermost system boundaries.
Exception Hiding
Wrong order can “hide” an important error in a general log. Monitoring won’t see the difference between ValidationException and OutOfMemoryError.
Static Analysis
Use Checkstyle or Sonar to guarantee a consistent approach to exception catching hierarchy across the entire project.
Diagnostics
javap -c- shows Exception Table with jump priorities tocatchlabels- Metrics - count each exception type separately
- Log Correlation - save Trace ID for all error types
- Global Error Handler -
@ControllerAdvicecatches everything not caught locally
Interview Cheat Sheet
Must know:
- Main rule: from specific to general - children before parents in Exception Table
- Compiler won’t allow reverse order - error “exception X has already been caught”
- JVM scans Exception Table top to bottom, first match captures control (first match wins)
- Order within
catch (A | B e)does NOT matter - all types point to one target - Exceptions not related by inheritance can be arranged in any order between themselves
- Place the most frequent exceptions first for micro-optimization of search
catch (Throwable t)- only at system boundary, never in business logic
Frequent follow-up questions:
- What happens if you mix up the order? - Compilation error, code won’t compile
- Does order affect performance? - O(n) search, frequent exceptions first
- When is order NOT important? - Multi-catch, non-inheritance-related types, single catch
- Why do we catch
Throwableonly at the end? - CatchesErrorincludingOutOfMemoryError
Red flags (NOT to say):
- “I put general catch first for convenience” - won’t compile
- “Order in multi-catch matters” - no, all types -> one target
- “I catch
Throwablein the middle of business logic” - catchesError, state is unpredictable - “Catch order affects correctness of compiled code” - compiler guarantees correct order
Related topics:
- [[24. Can you have multiple catch blocks for one try]] - multiple catches and first match wins
- [[25. What is multi-catch (catching multiple exceptions)]] - multi-catch removes ordering problem
- [[1. What is the difference between checked and unchecked exceptions]] - exception hierarchy
- [[5. What is at the top of the exception hierarchy]] - Throwable and Error
- [[19. Why you should not swallow exceptions (catch empty)]] - incorrect handling