Java Exception-Handling Best Practices — Anti-Patterns and Their Fixes
Fast recall
Quick revision
Core answer
Exception or Throwable as a blanket — catch the narrowest type; (2) never swallow with empty catch — at minimum log with context; (3) always preserve the cause via throw new X(msg, cause); (4) throw early, catch at boundaries — validate inputs with IllegalArgumentException, handle at the request/transaction boundary; (5) prefer unchecked custom exceptions with structured fields (orderId, errorCode) for REST error mapping; (6) use try-with-resources for any AutoCloseable; (7) don't use exceptions for control flow — stack trace capture is expensive; (8) in Spring, use @ControllerAdvice + @ExceptionHandler so domain exceptions map to structured HTTP responses; (9) never catch InterruptedException silently — restore the interrupt flag or rethrow.Whether the candidate knows the concrete anti-patterns seniors watch for in code review — blanket catches, empty catches, lost causes, exceptions for control flow, broken interrupt handling — and can articulate the fix for each
Listing textbook best practices without explaining the bug each one prevents. Missing the InterruptedException handling rule. Not knowing that Spring's global exception handlers are the right place to translate domain exceptions to HTTP responses
Give the translation pattern at layer boundaries — repository catches `SQLException` and throws domain-specific `UserNotFoundException`; controller-advice maps to HTTP 404. Mention the performance cost of exception creation (stack capture), and why using them for control flow like parsing-to-validate is expensive
Interview-ready guide
Interview answer
The way I frame exception handling is "throw early, catch at boundaries" — lower layers throw specific domain exceptions with the cause preserved, and a single boundary handler translates them to responses.
The catch-side anti-patterns I look for in code review
- Blanket
catch (Exception e)outside the request boundary — hides bugs - Empty catch block — the failure disappears; at minimum log with context
- Losing the cause on rethrow — always pass
causeto the constructor; the stack trace is the only artifact production debugging has - Silently catching
InterruptedException— restore the flag withThread.currentThread().interrupt()or rethrow; otherwise cancellation breaks
The throw-side anti-patterns
throws Exceptionon an API — defeats the contract; declare specific types- Exceptions for control flow — stack capture is expensive; validate explicitly instead
- Returning
nullor sentinel-1for failures — makes every caller remember a check; useOptionalfor expected-absence, exceptions for real failures - Wrapping every checked exception in bare
RuntimeException— caller loses the ability to catch precisely; use a domain-specific unchecked type
The pattern that works
- Repository catches
SQLException, throwsOrderNotFoundException(id, cause) - Service has no try-catch — just lets exceptions propagate
@ControllerAdvicecatches domain exceptions and returns structured JSON with the right HTTP status- Try-with-resources for every
AutoCloseable— suppressed-exception handling is free
Recommendation: Default to unchecked custom exceptions with preserved causes plus a global `@ControllerAdvice` handler. Reserve checked exceptions for APIs where the caller genuinely has a recovery path. Never swallow, never lose the cause, never use exceptions for control flow, and always restore the interrupt flag on InterruptedException.
Java Exception-Handling Best Practices — Anti-Patterns and Their Fixes
Direct Answer
The non-negotiables: (1) never catch Exception or Throwable as a blanket — catch the narrowest type; (2) never swallow with empty catch — at minimum log with context; (3) always preserve the cause via throw new X(msg, cause); (4) throw early, catch at boundaries — validate inputs with IllegalArgumentException, handle at the request/transaction boundary; (5) prefer unchecked custom exceptions with structured fields (orderId, errorCode) for REST error mapping; (6) use try-with-resources for any AutoCloseable; (7) don't use exceptions for control flow — stack trace capture is expensive; (8) in Spring, use @ControllerAdvice + @ExceptionHandler so domain exceptions map to structured HTTP responses; (9) never catch InterruptedException silently — restore the interrupt flag or rethrow.
Common Mistakes & Pitfalls
Listing textbook best practices without explaining the bug each one prevents. Missing the InterruptedException handling rule. Not knowing that Spring's global exception handlers are the right place to translate domain exceptions to HTTP responses
Check your understanding
Answer these without looking back at the article.
- How do you handle exceptions inside a Spring `@Async` method — why does try-catch on the caller not work?
- Why is catching `InterruptedException` and doing nothing considered a correctness bug?
- How does `@ControllerAdvice` with `@ExceptionHandler` compare to `HandlerExceptionResolver` in Spring MVC?
- When is it appropriate to log and rethrow vs catch-and-convert vs just let it propagate?
- How expensive is exception construction in Java, and what does `-XX:-OmitStackTraceInFastThrow` change?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.