Skip to main content

Exceptions Overview

Java's exception mechanism turns failure into a first-class part of the type system. Understanding the Throwable hierarchy, checked vs. unchecked exceptions, try-with-resources, and exception design patterns is non-negotiable for senior Java roles — it appears in every Spring Boot application and is a recurring interview topic.

Key Concepts at a Glance

  • Throwable: the root of the exception hierarchy; every thrown object must be a Throwable instance.
  • Error: signals unrecoverable JVM-level problems (OutOfMemoryError, StackOverflowError); should never be caught in application code.
  • Exception: the root of all application-level exceptions; subtypes are split into checked and unchecked.
  • Checked exception: any Exception that is NOT a RuntimeException; the compiler forces the caller to handle or declare it.
  • Unchecked exception: RuntimeException and its subclasses; the compiler does not enforce handling — models programming bugs and precondition violations.
  • RuntimeException: the root of all unchecked exceptions; models programming errors that could theoretically happen anywhere.
  • try/catch/finally: structured exception handling block; finally always runs regardless of whether an exception was thrown.
  • Multi-catch (Java 7+): catch (IOException | SQLException e) handles multiple types in a single block; e is implicitly final.
  • try-with-resources (Java 7+): automatically closes AutoCloseable resources in reverse order; handles suppressed exceptions correctly.
  • Suppressed exception: when both the try body and close() throw, the close() exception is attached to the primary via addSuppressed() instead of replacing it.
  • Exception chaining: preserving the original cause when wrapping — new ServiceException("msg", originalException).
  • Custom exception: application-specific subclass of RuntimeException (or Exception) carrying typed fields for machine-readable context.
  • Fail-fast: validate inputs at method entry with Objects.requireNonNull() or explicit if + throw so bad data is rejected immediately.
  • @ControllerAdvice: Spring Boot's global exception handler — maps domain exceptions to HTTP responses in one place.
  • InterruptedException: must never be swallowed; always call Thread.currentThread().interrupt() after catching it to restore the interrupt flag.

Quick-Reference Table

Concept / APIPurposeKey Note
throws IOExceptionDeclares checked exception in method signatureCallers must catch or re-declare
throw new XyzException(msg, cause)Throw an exception with context and causeAlways include cause when wrapping
catch (A | B e)Multi-catch: single handler for multiple typese is implicitly final
try (Resource r = ...)try-with-resources: auto-close any AutoCloseableClose in reverse order; suppresses close exceptions
e.getCause()Retrieve the wrapped original exceptionReturns null if no cause was set
e.getSuppressed()Retrieve suppressed close exceptionsSet by try-with-resources; can also use addSuppressed()
Objects.requireNonNull(x, "msg")Fail-fast null check at method entryThrows NullPointerException with clear message
Thread.currentThread().interrupt()Restore interrupt flag after catching InterruptedExceptionRequired for cooperative thread shutdown
@ExceptionHandler(XyzException.class)Spring: maps exception type to handler method in @ControllerAdviceMore specific handler wins
AppException extends RuntimeExceptionBase custom exception with HTTP status + error code fieldsUse hierarchy: catch base for generic, specific for targeted

Learning Path

Suggested reading order for a returning Java developer:

  1. Exception Hierarchy — understand the full Throwable tree, Error vs. Exception, and the checked/unchecked contract before anything else
  2. try/catch/finally — mechanics of handling exceptions: catch order, finally guarantees, multi-catch, and try-with-resources
  3. Custom Exceptions — build domain-specific exception types with typed fields and a two-level hierarchy
  4. Exception Best Practices — when to catch vs. propagate, log-once pattern, anti-patterns, fail-fast, and InterruptedException

Top 5 Interview Questions

Q1: What is the difference between Error, Exception, and RuntimeException?
A: All three extend Throwable. Error models unrecoverable JVM failures — never catch it. Exception (minus RuntimeException subtypes) is checked — the compiler forces handling. RuntimeException and its subtypes are unchecked — the compiler allows them to propagate without declaration, and they model programming bugs or precondition violations.

Q2: What is the advantage of try-with-resources over finally?
A: With a manual finally, if both the try body and close() throw, the finally's exception replaces the primary one — the original failure is permanently lost. try-with-resources instead attaches the close() exception as a suppressed exception on the primary, so diagnostics are never lost. It's also less code and handles multiple resources in reverse-declaration order automatically.

Q3: When should you create a custom exception instead of reusing JDK types?
A: When the failure mode is domain-specific and callers need to distinguish it by type. If IllegalArgumentException precisely describes the failure, reuse it. If you need InsufficientFundsException or OrderExpiredException with typed fields like orderId or requiredAmount, create a custom one. Custom exceptions eliminate string parsing in catch blocks and enable precise HTTP mapping in @ControllerAdvice.

Q4: Why must InterruptedException never be swallowed silently?
A: InterruptedException clears the thread's interrupt flag when it is thrown. Swallowing it (catch (InterruptedException e) {}) permanently destroys that signal. ExecutorService.shutdownNow() uses thread interrupts to signal workers to stop — if they swallow InterruptedException, they never terminate and the executor hangs indefinitely. Always call Thread.currentThread().interrupt() after catching it.

Q5: How should exception handling be structured in a layered Spring Boot application?
A: The repository translates infrastructure exceptions (SQLException) into domain exceptions at the boundary (once). The service layer neither catches nor declares them — it propagates transparently. Controllers contain no try/catch. A @RestControllerAdvice global handler catches all subtypes of your base AppException, maps them to HTTP responses using typed fields (status, error code), and logs them once. Every exception is handled exactly once, at the outermost boundary.


All Notes in This Domain

NoteDescription
Exception HierarchyThrowable tree — Error, Exception, RuntimeException; checked vs. unchecked
try/catch/finallyCatch order, multi-catch, finally guarantees, try-with-resources, suppressed exceptions
Custom ExceptionsTyped fields, hierarchy design, exception chaining, Spring @ControllerAdvice integration
Exception Best PracticesGolden rules, anti-patterns, fail-fast, InterruptedException, log-once pattern

  • Core Java Overview — foundations including control flow; throw/try/catch are part of the language syntax
  • OOP Overview — custom exceptions are classes; understanding inheritance is a prerequisite for exception hierarchies