BackendBench
Coffee

Unravel โ€บ Java concepts โ€บ Errors and logging

Errors and logging

Custom exceptions: checked vs unchecked

A checked exception (extends Exception) must be caught or declared with throws; an unchecked one (extends RuntimeException) need not.

Learn these first

At a glance

Mental model

A checked exception is a "fragile, handle with care" parcel the carrier must declare at every handover.

What happens, in order
  1. 1class OutOfStockException extends RuntimeException { OutOfStockException(String sku) { super("no stock for " + sku); } } is a complete custom exceptionAdd fields (the sku, the requested amount) so handlers can react without parsing the message.
  2. 2Checked exceptions (IOException, SQLException, your own extends Exception) force callers to handle or declare themThe compiler enforces that something is done about "expected" failures.
  3. 3Unchecked exceptions (IllegalArgumentException, NullPointerException, your own runtime exceptions) signal bugs or failures that callers usually cannot fix locally; frameworks catch them at the top
  4. 4Modern style (Spring, most libraries) uses mostly unchecked exceptions with a clear name, and a central handler that maps them to responses
Remember
Watch out
Making every custom exception checkedThrowing new Exception("error") with no cause or detailCatching Exception and throwing away the stack traceForgetting to pass the cause
Example (highlighted lines explain it)
public class Stock {  public static class OutOfStockException extends RuntimeException {    private final String sku;    public OutOfStockException(String sku, Throwable cause) { super("no stock for " + sku, cause); this.sku = sku; }    public String sku() { return sku; }  }  public static class SupplierException extends Exception {    public SupplierException(String msg) { super(msg); }  }  void order(String sku, int n) throws SupplierException {    if (n <= 0) throw new IllegalArgumentException("n must be positive");    throw new SupplierException("supplier offline");  }}
Read the full explanation (about 2 min)

A checked exception (extends Exception) must be caught or declared with throws; an unchecked one (extends RuntimeException) need not. You define your own to give a failure a precise name and carry useful details.

A mental model

A checked exception is a "fragile, handle with care" parcel the carrier must declare at every handover. An unchecked exception is a fire alarm: it goes off wherever it happens, and the building's emergency procedure deals with it.

How it works

class OutOfStockException extends RuntimeException { OutOfStockException(String sku) { super("no stock for " + sku); } } is a complete custom exception. Add fields (the sku, the requested amount) so handlers can react without parsing the message.

Checked exceptions (IOException, SQLException, your own extends Exception) force callers to handle or declare them. The compiler enforces that something is done about "expected" failures.

Unchecked exceptions (IllegalArgumentException, NullPointerException, your own runtime exceptions) signal bugs or failures that callers usually cannot fix locally; frameworks catch them at the top.

Modern style (Spring, most libraries) uses mostly unchecked exceptions with a clear name, and a central handler that maps them to responses.

Going deeper

Choose by what the caller can do: if a sensible caller can recover (retry, ask the user again), checked is defensible; if not, use unchecked. Do not declare throws Exception on everything, which defeats the checking.

Always pass the original exception as the cause (super(message, cause)) so the root problem survives. Add a serialVersionUID only if you really serialize it.

Do not use exceptions for normal control flow (end of loop, "not found" in a hot path): they are slow compared to a return value, and they hide the real flow.

Overriding methods can only throw the same or narrower checked exceptions than the parent method; that is why checked exceptions leak awkwardly through interfaces and lambdas.

Under the hood

The compiler enforces checked exceptions at compile time only; the JVM does not distinguish them. The cost of an exception is mostly filling in the stack trace (fillInStackTrace); exceptions that never need one can override it for speed.

Common mistakes

  • Making every custom exception checked.
  • Throwing new Exception("error") with no cause or detail.
  • Catching Exception and throwing away the stack trace.
  • Forgetting to pass the cause.

How Unravel recognises it

When you paste code, Unravel looks for these signs of custom exceptions: checked vs unchecked. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Frequently asked questions

What is Custom exceptions: checked vs unchecked?

A checked exception (extends Exception) must be caught or declared with throws; an unchecked one (extends RuntimeException) need not. You define your own to give a failure a precise name and carry useful details.

What are the most common mistakes with Custom exceptions: checked vs unchecked?

Making every custom exception checked. Throwing new Exception("error") with no cause or detail. Catching Exception and throwing away the stack trace. Forgetting to pass the cause.

How can I see Custom exceptions: checked vs unchecked in real code?

Paste your own Java into Unravel and it will point out where custom exceptions: checked vs unchecked appears, explain how it works at the detail level you choose, and draw the flow of the code around it.