When defining a custom exception in Java, how do you decide whether to extend Exception (checked) or RuntimeException (unchecked)?
answer
- Exception = checked = handle-or-declare; RuntimeException = unchecked
- Checked for recoverable; unchecked for bugs/preconditions
- IOException checked vs IllegalArgumentException unchecked
- Spring wraps SQLException into unchecked DataAccessException
- Never extend Throwable/Error for app exceptions
basics
~10 sExtend Exception for a checked exception that callers must handle or declare; extend RuntimeException for an unchecked one that needs no try/catch or throws clause. Use checked for recoverable errors, unchecked for programming bugs.
solid answer
~40 sA custom exception extends either Exception or RuntimeException. Extending Exception makes it checked: the compiler forces callers to catch it or declare it with throws, which is good for recoverable conditions a caller can reasonably react to. Extending RuntimeException makes it unchecked: no compiler obligation, suited to programming errors or violated preconditions (like invalid arguments or illegal state) where catching everywhere adds noise. The practical guideline is: if the caller can plausibly recover, lean checked; if it signals a bug or an unrecoverable situation, lean unchecked. Many modern codebases and frameworks (Spring, for example) favor unchecked exceptions to avoid throws-clause pollution and leaky abstractions. Whichever you pick, extend the right base so the type carries the correct compile-time contract; never extend Throwable or Error directly for application exceptions.
go deeper
Knows Exception is checked and RuntimeException is unchecked, and that checked ones need try/catch or throws.
Can articulate the recoverable-vs-bug guideline and give JDK examples (IOException vs IllegalArgumentException), and pick the right base for a given scenario.
Weighs the trade-offs (signature pollution, abstraction leakage, lambda interaction) and explains why frameworks lean unchecked; sets a consistent project convention.
Defines org-wide exception strategy and translation boundaries between layers, balancing API contract clarity against checked-exception fatigue, and documents the rationale.
## Background: the exception hierarchy In Java, everything you can `throw` descends from the class **`Throwable`**. `Throwable` has two direct subclasses: - **`Error`** — serious problems the application normally cannot recover from (e.g. `OutOfMemoryError`, `StackOverflowError`). You do not create or catch these in normal code. - **`Exception`** — conditions an application might want to handle. Under `Exception` there is a special subclass, **`RuntimeException`**. This split defines two categories: - **Checked exceptions**: any subclass of `Exception` that is *not* a subclass of `RuntimeException`. - **Unchecked exceptions**: `RuntimeException` and its subclasses (plus `Error`). ## What "checked" actually means "Checked" refers to a **compile-time rule** enforced by the Java compiler, called the *handle-or-declare* requirement. If a method body can throw a checked exception, the method must either: 1. **catch** it in a `try`/`catch` block, or 2. **declare** it in the method signature with a `throws` clause, e.g. `void load() throws IOException`. If you do neither, the code does not compile. Unchecked exceptions have **no** such requirement — you can throw them anywhere and callers are never forced by the compiler to do anything. ## So which base class do you extend? When you write `class MyException extends Exception`, your exception is **checked**. When you write `class MyException extends RuntimeException`, it is **unchecked**. That single choice of superclass is what determines the compile-time contract. ### The decision guideline The classic guidance (from *Effective Java* and the Java tutorials) is: > Use **checked** exceptions for **recoverable** conditions — situations where a reasonable caller can do something useful in response (retry, fall back, ask the user). The checked contract *forces* the caller to acknowledge the possibility. > > Use **unchecked** (`RuntimeException`) exceptions for **programming errors** and precondition violations — bugs like passing `null` where it is forbidden, or calling a method in the wrong state. The caller usually cannot recover from a bug; forcing try/catch everywhere only adds clutter. Examples from the JDK reinforce this: `IOException` (a file might genuinely be missing — recoverable) is checked; `IllegalArgumentException` and `IllegalStateException` and `NullPointerException` (bugs) are unchecked. ### Practical trade-offs - **Checked pros:** the compiler guarantees the caller is aware of and addresses the failure mode; the failure is part of the documented API. - **Checked cons:** they propagate up through every signature, polluting `throws` clauses, and they leak implementation details across abstraction layers (a high-level method shouldn't have to declare a low-level `SQLException`). They also interact badly with lambdas/streams, whose functional interfaces don't declare checked exceptions. - **Unchecked pros:** clean signatures, easy composition, work with lambdas. - **Unchecked cons:** nothing reminds the caller the exception exists, so it can slip through unhandled. Because of the cons of checked exceptions, many modern frameworks (notably **Spring**, which wraps `SQLException` into the unchecked `DataAccessException`) and many style guides default to **unchecked** custom exceptions, reserving checked ones for cases where forcing handling genuinely adds value. ## Rules of thumb to remember - Extend `Exception` → checked → "caller must deal with it" → recoverable conditions. - Extend `RuntimeException` → unchecked → "this is a bug or unrecoverable" → no compiler ceremony. - Never extend `Throwable` or `Error` directly for your own application exceptions. - Be consistent within a codebase/module; mixing arbitrarily confuses callers.
- Why do many modern frameworks prefer unchecked exceptions?Checked exceptions pollute method signatures with throws clauses, leak lower-layer details up the stack, and don't compose with lambdas/streams. Unchecked exceptions keep APIs clean while still letting callers catch when they want to.
- Can you catch a RuntimeException?Yes. Unchecked simply means the compiler doesn't force you to catch or declare it; you can still write a try/catch for it at any point you choose.
saying these in an interview costs you the question
- Saying checked vs unchecked is a runtime distinction — it is a compile-time handle-or-declare rule
- Claiming you must extend Throwable directly to make a custom exception
- Believing unchecked exceptions cannot be caught (they can; you're just not forced to)
- Thinking the choice affects performance rather than the API contract