skip to content

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

level: middleimportance: should knowfreq 55%

answer

  1. Walk the superclass chain to the first marker class
  2. RuntimeException subtree -> unchecked
  3. Error subtree -> unchecked
  4. Exception but not RuntimeException -> checked
  5. InterruptedException / ClassNotFoundException are checked (surprises)

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.

solid answer

~40 s

Classification is purely about the position in the Throwable hierarchy, not about how the exception is used. Walk up the superclass chain: if you reach RuntimeException before reaching plain Exception, it's unchecked. If you reach Error, it's also unchecked. If you reach Exception without passing through RuntimeException, it's checked. So Throwable and Exception sit on the boundary: a direct subclass of Exception is checked, but RuntimeException (itself a direct subclass of Exception) and everything below it is unchecked. Practical examples: FileNotFoundException extends IOException extends Exception, so checked. NumberFormatException extends IllegalArgumentException extends RuntimeException, so unchecked. A custom class that extends Exception is checked; extend RuntimeException to make it unchecked. The category is decided when the class is declared, by what it extends.

go deeper

for a junior

Can apply the rule for the common cases (IOException checked, NullPointerException unchecked).

for a middle

Can walk the hierarchy for unfamiliar classes and knows the surprising checked ones like InterruptedException.

for a senior

Explains that category is fixed at declaration by the chosen superclass, not by usage, and can decide what a custom exception should extend.

for a principal

Can reason about boundary classes (Throwable/Exception checked; RuntimeException/Error unchecked) and the implications for broad catch clauses in framework code.

## The single rule Whether an exception is checked or unchecked is **decided entirely by its class hierarchy** — specifically by which special base class appears in its chain of ancestors. There is no annotation, keyword, or runtime flag; you read it off the `extends` relationships. The top of the throwable world looks like this: ``` Throwable ├── Error -> all subclasses UNCHECKED └── Exception -> direct subclasses CHECKED ... └── RuntimeException -> ... EXCEPT this subtree, which is UNCHECKED ``` ## How to classify any class: walk up the chain Start at the class and follow its `extends` links upward until you hit one of the three marker classes: 1. If you reach **`RuntimeException`** first → **unchecked**. 2. Else if you reach **`Error`** first → **unchecked**. 3. Else if you reach **`Exception`** (without having passed through `RuntimeException`) → **checked**. 4. (If a class extends `Throwable` directly but neither `Exception` nor `Error`, it is treated as checked — but this is extremely rare and discouraged.) ## Worked examples - `FileNotFoundException` → `IOException` → `Exception`. Never passes through `RuntimeException`, so **checked**. - `NumberFormatException` → `IllegalArgumentException` → `RuntimeException`. Passes through `RuntimeException`, so **unchecked**. - `StackOverflowError` → `VirtualMachineError` → `Error`. **Unchecked**. - `InterruptedException` → `Exception`. **Checked** (a common surprise — it is checked). - `ClassNotFoundException` → `Exception`. **Checked** (surprising, since reflection feels low-level). ## Why 'how it is used' doesn't matter A frequent misconception is that an exception is checked 'because you have to handle it' — but that is backwards. The catch-or-declare obligation is a *consequence* of the category, and the category is fixed at class-declaration time by the chosen superclass. You make your **own** exception checked or unchecked simply by choosing what to extend: ```java class PaymentDeclinedException extends Exception {} // checked class InvalidConfigException extends RuntimeException {} // unchecked ``` ## Determining it programmatically At runtime you can test the category with `instanceof`: ```java boolean isUnchecked(Throwable t) { return (t instanceof RuntimeException) || (t instanceof Error); } ``` Anything for which this returns `false` (and that is a `Throwable`) is checked. ## Edge note on the boundary classes `Throwable` and `Exception` are themselves *checked* — if a method declared `throws Exception`, callers must handle it. `RuntimeException` and `Error` are the unchecked roots. This is why catching `Exception` catches both checked and unchecked subtypes, while catching `RuntimeException` catches only the unchecked-runtime subtree (not `Error`).

  • Is InterruptedException checked or unchecked?
    Checked — it extends Exception directly, not RuntimeException. That's why blocking calls like Thread.sleep force you to handle or declare it.
  • Does catching RuntimeException also catch Error?
    No. Error is a separate sibling subtree under Throwable. To catch both unchecked families plus checked ones you would catch Throwable (rarely advisable).

saying these in an interview costs you the question

  • Saying an exception is checked because you 'have to handle it' (cause and effect reversed)
  • Forgetting RuntimeException is itself a subclass of Exception
  • Assuming low-level-sounding exceptions like InterruptedException are unchecked
  • Thinking catching Exception does not catch RuntimeException (it does)

context