All topics
exceptions
beginner

Checked vs Unchecked Exceptions

Choose between forcing callers to handle a failure (checked) and letting them propagate it (unchecked).

An exception is Java's way of reporting that something went wrong. Java has two kinds. A checked exception must be caught or declared, and the compiler refuses to build your code until you do. An unchecked exception needs neither.

Checked exception = a contract clause you must initial. Unchecked = a warning sign — you ignore it at your own risk.

Key Concepts

1
The interview question is which kind you should use for your own errors, and why.
2
Java is the only mainstream language that kept checked exceptions, and the interview question is really about when you force a caller to deal with a failure and when you let it propagate.
3
The hierarchy decides the rules. Throwable splits into Error and Exception. Error is for conditions you cannot sensibly handle — OutOfMemoryError, StackOverflowError. Under Exception, RuntimeException and its subclasses are unchecked; everything else is checked, which means the compiler requires you to catch it or declare it.
ThrowableErrorExceptionOutOfMemoryErrorStackOverflowError
4
Checked means the caller has a decision to make. A file may be absent, a network may be unreachable — recoverable conditions where a well-written caller has a fallback. Unchecked means a programming error: a null dereference, an index past the end, an illegal argument. The distinction is about who can fix it, not about severity.
5
The two failure modes are both common. Over-using checked exceptions produces signatures littered with throws and callers who write catch (Exception e) {} to make the compiler stop complaining — which destroys the information the exception carried. Under-using them hides recoverable failures until runtime. The modern consensus leans unchecked for most application code, and being able to argue both sides matters more than which side you pick.
throwscatch (Exception e) {}
6
Swallowing is the cardinal sin. An empty catch block discards evidence of a failure that already happened; the program continues in an unknown state and the incident surfaces later somewhere unrelated. If you truly intend to ignore an exception, say so in a comment explaining why, and log at least once.
7
Wrapping must preserve the cause. throw new ServiceException("Failed to load user") without the cause loses the stack trace that says why. throw new ServiceException("Failed to load user " + id, e) keeps it, and the chained trace shows both layers. This is the single highest-value habit in exception handling, because it is what makes a production log actionable.
8
Exceptions are expensive if you use them for flow. Constructing one captures the stack trace, which costs roughly a microsecond — trivial once, significant in a loop running thousands of times a second. Using exceptions for expected outcomes (a cache miss, a validation result) is both slow and misleading; return a value or an Optional instead.
Optional
9
What the interviewer is probing.1. "Checked or unchecked — how do you choose?" Probing: reasoning, not recall. Stalls: "Checked for IO." Moves up: can the caller do something about it; recoverable condition versus programming error.
10
2. "What is wrong with catch (Exception e) {}?" Probing: the cardinal sin. Stalls: "It's untidy." Moves up: it destroys evidence and lets the program continue in an unknown state; the incident then appears far from its cause.
catch (Exception e) {}
11
3. "How do you wrap an exception?" Probing: cause preservation. Stalls: "Throw a new one." *Moves up:* always pass the cause, so the chained trace shows both layers.
12
4. "Is finally always executed?" Probing: precision. Stalls: "Yes." Moves up: yes, except on System.exit, JVM crash or thread kill — and a return inside finally silently discards a pending exception.
finallySystem.exitreturn
13
5. "Where do exceptions cost?" Probing: calibration. Stalls: "They're slow." Moves up: stack capture at construction, ~1 microsecond; negligible once, real in a hot loop — so never use them for expected flow.
14
  The hierarchy
15
    Throwable
      +-- Error                      unchecked, do not catch
      |     OutOfMemoryError, StackOverflowError,
      |     NoClassDefFoundError
      +-- Exception                  CHECKED (except below)
            IOException, SQLException, InterruptedException
            +-- RuntimeException     unchecked
                  NullPointerException, IllegalArgumentException,
                  IllegalStateException, IndexOutOfBoundsException
16
  Choosing
17
    Can the caller recover?    yes -> consider checked
    Programming error?         yes -> unchecked, always
    Will every caller just
      rethrow it?              yes -> unchecked, or the signature
                                      pollutes the whole call chain
18
  The finally traps
19
    try { return "a"; } finally { return "b"; }   // returns "b",
                                                 // and swallows any
                                                 // pending exception
20
    try { throw new IOException(); }
    finally { return "x"; }                       // exception is LOST
21
    -> never return or throw from finally.
22
  Wrapping, correctly
23
    // loses the cause -- the log says what failed, not why
    catch (SQLException e) {
        throw new DataAccessException("query failed");
    }
24
    // keeps it -- the chained trace shows both layers
    catch (SQLException e) {
        throw new DataAccessException("query failed for user " + id, e);
    }
25
  Cost
    new Exception()  captures the stack   ~1 us
    in a loop at 10,000/s                 ~10 ms/s of pure overhead
    -> fine for failures, wrong for expected outcomes