exceptions
beginnerChecked 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 hierarchy15
Throwable
+-- Error unchecked, do not catch
| OutOfMemoryError, StackOverflowError,
| NoClassDefFoundError
+-- Exception CHECKED (except below)
IOException, SQLException, InterruptedException
+-- RuntimeException unchecked
NullPointerException, IllegalArgumentException,
IllegalStateException, IndexOutOfBoundsException16
Choosing17
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 chain18
The finally traps19
try { return "a"; } finally { return "b"; } // returns "b",
// and swallows any
// pending exception20
try { throw new IOException(); }
finally { return "x"; } // exception is LOST21
-> never return or throw from finally.22
Wrapping, correctly23
// 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