skip to content

What is the difference between checked and unchecked exceptions, and how does that distinction map onto the Throwable hierarchy?

level: middleimportance: must knowfreq 80%

answer

  1. Checked = compiler forces catch-or-declare
  2. Unchecked = RuntimeException OR Error (and subclasses)
  3. Checked = Exception minus the RuntimeException sub-tree
  4. Compile-time rule, JVM doesn't distinguish
  5. IOException/SQLException checked; NPE/IAE unchecked

basics

~10 s

Checked exceptions must be handled or declared with throws, or your code won't compile. Unchecked ones (RuntimeException and Error) don't force you to do anything. The compiler enforces checked exceptions; it ignores unchecked ones.

solid answer

~40 s

The checked/unchecked split is a compile-time rule based on position in the Throwable tree. Unchecked = anything that is a RuntimeException or an Error (and their subclasses); checked = everything else under Exception (e.g. IOException, SQLException). For a checked exception, the compiler forces the calling method to either catch it or list it in a throws clause; failing to do so is a compile error. For unchecked exceptions there is no such obligation — you can let a NullPointerException propagate without declaring it. The intent: checked exceptions model recoverable, expected conditions that should be visible in the API contract, while RuntimeExceptions model programming bugs and Errors model catastrophic JVM problems that shouldn't pollute every signature. Note this is purely a compiler convention; at runtime the JVM treats all throwables identically.

go deeper

for a junior

Knows checked exceptions must be caught or declared and gives the canonical example (IOException) versus an unchecked one (NullPointerException).

for a middle

States the exact boundary on the hierarchy (Error and RuntimeException unchecked; rest of Exception checked) and explains the catch-or-declare obligation accurately.

for a senior

Articulates the design intent behind the split and the trade-offs/criticisms of checked exceptions, including override rules and lambda friction.

for a principal

Sets team conventions for when to use checked vs unchecked in custom exceptions, weighs API coupling and maintainability, and understands how framework error-handling strategies are shaped by this choice.

## The core idea "Checked" and "unchecked" describe whether the **Java compiler forces you to deal with an exception**. This is a property determined entirely by *where a class sits in the `Throwable` hierarchy*, and it is enforced at *compile time* — the JVM at runtime makes no such distinction. ## Defining the boundary on the tree ``` Throwable ├── Error → UNCHECKED └── Exception → CHECKED ... └── RuntimeException → UNCHECKED (the carve-out) ``` The rule, precisely: - A class is **unchecked** if it is `Error`, or `RuntimeException`, or any subclass of either. - A class is **checked** if it is `Exception` (or a subclass) but **not** in the `RuntimeException` sub-tree. So `IOException`, `SQLException`, `InterruptedException`, `ClassNotFoundException` are checked. `NullPointerException`, `IllegalArgumentException`, `ArithmeticException`, `ClassCastException` are unchecked (they're RuntimeExceptions). `OutOfMemoryError`, `StackOverflowError` are unchecked (they're Errors). ## What "the compiler forces you" actually means If a method body can throw a *checked* exception, the compiler requires one of two things in the **calling** method: 1. **Handle it** — wrap the call in `try { ... } catch (TheCheckedException e) { ... }`, or 2. **Declare it** — add `throws TheCheckedException` to the method signature, passing the obligation up to *its* caller. If you do neither, the code does not compile ("unreported exception ... must be caught or declared to be thrown"). For an *unchecked* exception, none of this applies — you may catch it, declare it, or ignore it entirely, and the code compiles regardless. ```java // Checked: must catch or declare — this WON'T compile as-is void read() { new FileInputStream("x"); // throws FileNotFoundException (checked) } // Fix 1: declare void read() throws FileNotFoundException { new FileInputStream("x"); } // Unchecked: compiles with no try/catch and no throws void parse(String s) { Integer.parseInt(s); // may throw NumberFormatException (unchecked) } ``` ## Why the language designers drew the line here - **Checked exceptions** are meant for conditions that are *outside the program's control but anticipatable* — the file might be missing, the network might drop. Making them checked puts the failure mode into the method's published contract (its `throws` clause), forcing callers to consciously decide what to do. - **`RuntimeException` (unchecked)** is meant for *programming errors* — a null you forgot to guard, an index past the end of an array. In principle correct code wouldn't hit them, so the language doesn't burden every method with declaring them. You *fix the bug* rather than routinely catch them. - **`Error` (unchecked)** is for *grave, typically unrecoverable* JVM/environment failures. There's usually nothing useful to do, so forcing handling would be pointless ceremony. ## Trade-offs and debates Checked exceptions are somewhat controversial. Pros: explicit, self-documenting failure modes the compiler verifies. Cons: they couple callers to implementation details, encourage `throws Exception` blanket declarations or empty `catch` blocks that swallow problems, and interact poorly with lambdas/streams (functional interfaces like `Function` don't declare checked exceptions). Many modern frameworks (e.g. Spring) lean heavily on *unchecked* exceptions for this reason. Knowing the boundary lets you make a deliberate choice when designing your own exception types. ## Practical tells - See it in a `throws` clause and the compiler complains if you remove it → checked. - It compiles fine with no handling at all → unchecked (RuntimeException or Error).

  • Why do checked exceptions interact awkwardly with Java streams and lambdas?
    Standard functional interfaces (Function, Supplier, Consumer, etc.) don't declare any checked exceptions in their abstract method, so a lambda body that throws a checked exception won't compile inside them. You must catch-and-wrap into an unchecked exception, use a custom throwing functional interface, or a helper that rethrows — which is why checked exceptions feel clunky in functional code.
  • If you override a method, what are the rules about checked exceptions in the override?
    An overriding method may throw the same checked exceptions, narrower (subclass) ones, or fewer — but it cannot declare new or broader checked exceptions than the method it overrides. It may always add unchecked exceptions, since those aren't part of the checked contract.

saying these in an interview costs you the question

  • Saying Error is checked — it is unchecked
  • Equating 'unchecked' with 'RuntimeException only' and forgetting Error
  • Believing the JVM enforces checked exceptions at runtime (it's the compiler, at compile time)
  • Claiming you can't declare an unchecked exception in throws — you can, it's just not required
  • Thinking checked exceptions are 'more serious' than unchecked — severity isn't the axis; recoverability/origin is

context