skip to content

Checked vs Unchecked Exceptions

Checked exceptions are enforced by the compiler's catch-or-declare rule; RuntimeException and Error are not. Interviewers ask which you would use for a new API, and expect you to know the long-running criticism of checked exceptions as well as the rationale.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

5

What is the difference between a checked and an unchecked exception in Java?

level: juniorimportance: must knowfreq 85%

answer

  1. Checked = Exception but not RuntimeException; compiler-enforced
  2. Unchecked = RuntimeException subtree + Error subtree
  3. Catch-or-declare rule applies only to checked
  4. Checked = recoverable; unchecked = bugs / fatal JVM
  5. RuntimeException extends Exception but is carved out as unchecked

basics

~10 s

A checked exception must be either caught or declared with throws, and the compiler enforces this. An unchecked exception (RuntimeException or Error and their subclasses) is not enforced by the compiler.

solid answer

~40 s

Java splits exceptions into two compiler-enforced categories. A checked exception is any subclass of Exception that is NOT a subclass of RuntimeException (for example IOException, SQLException). For these the compiler enforces a 'catch-or-declare' rule: any code that might throw one must either catch it with try/catch or declare it in the method's throws clause, otherwise the code won't compile. An unchecked exception is RuntimeException and its subclasses (NullPointerException, IllegalArgumentException, etc.) plus Error and its subclasses (OutOfMemoryError). The compiler does not require you to catch or declare these. The practical idea: checked = recoverable conditions the caller should plan for; unchecked = programming bugs or fatal JVM problems you usually can't sensibly recover from at the call site.

go deeper

for a junior

Can state the catch-or-declare rule and give one example of each category (e.g. IOException checked, NullPointerException unchecked).

for a middle

Can place RuntimeException and Error correctly in the hierarchy and explain that 'checked' is a compile-time concept, not runtime.

for a senior

Can explain the design rationale (recoverable vs bug/fatal) and choose the right category when designing an API, plus translate exceptions across layers.

for a principal

Can argue the tradeoffs (boilerplate, leaky abstractions, lambda/stream friction), set a team-wide policy, and explain why modern libraries and Kotlin avoid checked exceptions.

## Background: what an exception is In Java, when something goes wrong at runtime, the program creates an **exception object** and 'throws' it. Throwing means normal execution stops and the JVM looks back up the chain of method calls (the **call stack**) for code that can **handle** the problem with a `try { ... } catch (SomeException e) { ... }` block. If no handler is found, the program crashes and prints a stack trace. ## The exception class hierarchy Everything throwable descends from the class `Throwable`. Below it there are two main branches: ``` Throwable ├── Error (e.g. OutOfMemoryError, StackOverflowError) -> UNCHECKED └── Exception ├── RuntimeException (e.g. NullPointerException, │ IllegalArgumentException, │ ArrayIndexOutOfBoundsException) -> UNCHECKED └── (everything else: IOException, SQLException, ...) -> CHECKED ``` The categorization rule is purely structural: - **Checked** = a subclass of `Exception` that is **NOT** a subclass of `RuntimeException`. - **Unchecked** = a subclass of `RuntimeException`, OR a subclass of `Error`. Note `RuntimeException` itself extends `Exception`, but it (and its descendants) are carved out as unchecked. ## What 'checked' actually means: the catch-or-declare rule The word 'checked' refers to a **compile-time** check the Java compiler performs. If a piece of code can throw a checked exception, the compiler forces you to do one of two things, or your code will not compile: 1. **Catch** it: wrap the call in `try { ... } catch (IOException e) { ... }`. 2. **Declare** it: add `throws IOException` to the enclosing method's signature, pushing the obligation onto whoever calls your method. This is called the **catch-or-declare** (or 'handle-or-declare') requirement. The compiler tracks, for every statement, which checked exceptions could escape, and verifies one of the two options is satisfied. Unchecked exceptions are exempt: you *may* catch them, but the compiler never *requires* it, and you don't have to list them in `throws`. ## Why two categories? The design rationale The original intent: **checked exceptions model recoverable, expected-but-abnormal conditions** that a well-written caller should consciously deal with — a file that doesn't exist, a network connection that drops. By forcing acknowledgment at compile time, the language tries to prevent you from silently ignoring these. **Unchecked exceptions model two other things:** - `RuntimeException` subclasses model **programming errors / bugs** — dereferencing null, passing an illegal argument, indexing past the end of an array. These can occur almost anywhere, so requiring `throws` on every method would be unbearable; and the right fix is usually to correct the code, not to catch the exception. - `Error` subclasses model **serious, usually unrecoverable JVM-level problems** — running out of memory, stack overflow. Application code generally should not try to catch or recover from these. ## The criticism of checked exceptions Checked exceptions are controversial. Common complaints: - **Boilerplate**: long `throws` lists or `try/catch` clutter. - **Leaky abstractions**: a low-level `SQLException` propagates `throws SQLException` up through many layers that shouldn't know about SQL. - **Encourages swallowing**: developers under pressure write empty `catch` blocks (`catch (IOException e) {}`), which hides errors — worse than not checking at all. - **Poor fit with generics, lambdas, and streams**: functional interfaces like `Function` don't declare checked exceptions, so checked exceptions are awkward inside lambdas. Because of this, many modern Java libraries (and other JVM languages such as Kotlin) avoid checked exceptions entirely, wrapping them in unchecked exceptions instead. Java itself never added new checked-exception-style features after the early years. ## Practical guidance - Throw a **checked** exception when the caller can realistically **recover** and you want to *force* them to consider it. - Throw an **unchecked** exception for **programming errors** (invalid arguments, illegal state) and when forcing handling would only add noise. - When crossing an architectural boundary, **translate** a low-level checked exception into a domain-specific exception (often unchecked) so callers don't depend on implementation details — but keep the original as the **cause** (`new MyException("msg", e)`) so the stack trace is preserved.

  • Is RuntimeException checked or unchecked, given that it extends Exception?
    Unchecked. The rule isn't 'extends Exception' alone — RuntimeException and all its subclasses are explicitly carved out of the checked category, so the compiler does not enforce catch-or-declare for them.
  • Where does Error fit?
    Error and its subclasses are unchecked. They represent serious, typically unrecoverable JVM problems (OutOfMemoryError, StackOverflowError) that application code normally should not catch.

saying these in an interview costs you the question

  • Saying unchecked exceptions can't be caught (they can; you just aren't forced to)
  • Claiming Error is a checked exception (it is unchecked)
  • Saying the difference is decided at runtime (it is a compile-time check)
  • Thinking RuntimeException is checked because it extends Exception
  • Believing throws is required for unchecked exceptions

context

open as a page

Given an arbitrary throwable class, how do you determine whether it is checked or unchecked?

level: middleimportance: should knowfreq 55%

basics

~10 s

Look at its ancestry. If it is a subclass of RuntimeException or of Error, it is unchecked. If it is a subclass of Exception but not of RuntimeException, it is checked.

open as a page

When should you design an API to throw a checked exception versus an unchecked one?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Use a checked exception when the caller can reasonably recover and you want to force them to handle it. Use an unchecked exception for programming errors (bad arguments, illegal state) and conditions the caller usually can't fix.

open as a page

What rule governs the checked exceptions an overriding method may declare in its throws clause?

level: seniorimportance: nice to knowfreq 35%

basics

~10 s

An overriding method cannot declare new or broader checked exceptions than the method it overrides. It may declare the same ones, narrower (subclass) ones, fewer, or none. Unchecked exceptions are unrestricted.

open as a page

Why are checked exceptions controversial, and how do modern designs work around their drawbacks?

level: principalimportance: nice to knowfreq 30%

basics

~10 s

Checked exceptions add boilerplate, leak low-level details up through layers, tempt developers to swallow errors, and don't fit lambdas/streams. Modern designs prefer unchecked exceptions, wrap checked ones, and translate them at boundaries.

open as a page