Skip to main content

Week 4 Day 1 — Throwable, Checked, and Unchecked

Goal

Today I want the exception type tree, so “checked vs unchecked” is a compiler rule I can draw, not a slogan.

Main questions:

  1. What sits under Throwable?
  2. What is an Error?
  3. What is a checked exception?
  4. What is an unchecked exception?
  5. Why does this split exist?

1. The type tree

Throwable
├── Error // JVM / environment, usually fatal
│ OutOfMemoryError
│ StackOverflowError
│ NoClassDefFoundError
└── Exception
├── RuntimeException // unchecked
│ NullPointerException
│ IllegalArgumentException
│ IllegalStateException
└── (everything else) // checked
IOException
SQLException
InterruptedException

I throw a Throwable. I almost never throw an Error. Application code throws Exception subtypes.

Memory sentence:

Error is the JVM in trouble. RuntimeException is unchecked. Other Exception types are checked.


2. Error

An Error means the process is in a bad place: no memory, stack overflow, a class that cannot load.

I do not catch Error to “recover” in a service method. Catching OutOfMemoryError and continuing is a way to corrupt more state.

Error is unchecked: the compiler does not force throws or catch. That is the same compiler treatment as RuntimeException, with a different meaning.


3. Checked exceptions

A checked exception is an Exception that is not a RuntimeException.

If a method can throw one, every caller must catch it or declare throws. The compiler enforces that.

public String read(Path path) throws IOException {
return Files.readString(path);
}

The idea: I/O, JDBC, and similar calls have failures the caller must notice at compile time. IOException is part of the method’s contract, like the return type.

Cost: every layer above must mention IOException or wrap it. That is why Spring and most domain services translate checked library exceptions into unchecked ones (Day 4).


4. Unchecked exceptions

RuntimeException and its subtypes are unchecked. I can throw them without throws. Callers may catch them. They are not forced to.

Typical uses:

  • programming mistakes: NullPointerException, IndexOutOfBoundsException
  • bad input: IllegalArgumentException
  • object in the wrong state: IllegalStateException
  • domain failures I will map at the HTTP boundary: OrderNotFoundException extends RuntimeException

A REST service that throws OrderNotFoundException does not want every method between the repository and the controller to declare throws. Unchecked keeps the happy path readable. A @ControllerAdvice still handles it.

Memory sentence:

Checked forces a compile-time decision. Unchecked means “this is a failure; handle it at a boundary or let it fail the request.”


5. Why the split exists — and why interviews care

Java’s designers wanted I/O-style failures to be visible. C# and Kotlin did not copy checked exceptions. Many Java teams now treat checked as a library concern and unchecked as an application concern.

Interview judgment, not dogma:

  • I do not say “never use checked” or “never use runtime.”
  • I use checked when I am writing a library method whose caller has a real recovery choice at compile time.
  • I use unchecked for domain and REST failures, then translate at the edge.
  • I never swallow either kind.

Spring’s DataAccessException is unchecked on purpose: so service code is not littered with SQLException.


6. Spring connection

  • Repository methods in Spring Data do not throw SQLException. They throw DataAccessException (unchecked).
  • @Transactional rolls back on unchecked by default, not on checked Exception. That trap is Day 5.
  • Controller advice maps exception types to HTTP status. The type tree is the mapping key.

7. Common traps

Trap 1:Error and RuntimeException are the same.”
Same compiler treatment (unchecked). Different meaning.

Trap 2: “Checked is always better because the compiler helps.”
If every layer only rethrows, the compiler helped nobody. Translate or declare once at a real boundary.

Trap 3: Catching Throwable in a controller.
That also catches Error. Too wide.

Trap 4: throws Exception on a service method.
That erases the type tree. Callers learn nothing.


Practice Questions and Answers

Question 1

What is a checked exception?

Answer:

An Exception that is not a RuntimeException. The compiler requires callers to catch it or declare throws. IOException and SQLException are the usual examples.


Question 2

Is NullPointerException checked?

Answer:

No. It extends RuntimeException, so it is unchecked. The compiler does not force throws. It still means a programming mistake: I dereferenced null.


Question 3

Should I catch OutOfMemoryError in a @Service?

Answer:

No. An Error means the JVM is not in a state I can reliably recover in business code. Let the process fail, and fix the leak or the heap size.


Question 4

Why does Spring wrap SQLException?

Answer:

SQLException is checked. If it leaked, every service method would declare throws SQLException without being able to recover. Spring translates it to an unchecked DataAccessException hierarchy so the service stays readable and a boundary can still map the failure.


Question 5

Checked vs unchecked — what does a REST service throw?

Answer:

Unchecked domain exceptions (OrderNotFoundException, IllegalArgumentException) and let the web layer map them to HTTP. I do not push IOException through the service API unless I am at a real I/O facade that can retry or fail the operation with a clear contract.


Memory sentences

Error is the JVM. RuntimeException is unchecked. Other Exception types are checked.

Checked forces a compile-time decision. Domain and REST code usually throw unchecked.

Do not catch Error. Do not declare throws Exception.

Next: Week 4 Day 2 — throw, throws, and catch