Java Exceptions β€” try-catch, throw, throws, checked vs unchecked

Exceptions are Java's mechanism for reporting that something went wrong β€” a missing file, a division by zero, a null where an object was expected. The compiler enforces handling for checked exceptions; unchecked ones (subclasses of RuntimeException) can be thrown and caught freely.

The exception hierarchy

Throwable
β”œβ”€β”€ Error                          // JVM problems β€” don't catch these
β”‚   β”œβ”€β”€ OutOfMemoryError
β”‚   └── StackOverflowError
└── Exception                      // your problems
    β”œβ”€β”€ IOException                // checked
    β”œβ”€β”€ SQLException               // checked
    └── RuntimeException           // unchecked
        β”œβ”€β”€ NullPointerException
        β”œβ”€β”€ ArrayIndexOutOfBoundsException
        β”œβ”€β”€ ClassCastException
        └── IllegalArgumentException

try / catch / finally

try {
    doRisky();
} catch (FileNotFoundException e) {
    log.warn("missing", e);
} catch (IOException | SQLException e) {      // multi-catch β€” Java 7+
    log.error("IO or DB", e);
} finally {
    cleanup();                                 // always runs
}

Try-with-resources (Java 7+) β€” the only safe way to close things

try (var reader = Files.newBufferedReader(path)) {
    return reader.readLine();
}
// reader.close() runs automatically β€” even on exception

Any object implementing AutoCloseable can go in the try resource list. Multiple resources are closed in reverse order.

Checked vs unchecked

CheckedUnchecked
ParentException (not RuntimeException)RuntimeException
Compiler-enforcedYes β€” must catch or declareNo
Typical useRecoverable, external I/O, networkProgramming bugs, invalid state
ExampleIOExceptionNullPointerException

throw vs throws

// throws β€” declares exceptions a method can throw (checked only)
public String read(Path p) throws IOException {
    if (!Files.exists(p)) {
        // throw β€” actually raises an exception
        throw new IllegalArgumentException("missing: " + p);
    }
    return Files.readString(p);
}

Custom exceptions

public class OrderNotFoundException extends RuntimeException {
    private final long orderId;
    public OrderNotFoundException(long orderId) {
        super("Order not found: " + orderId);
        this.orderId = orderId;
    }
    public long orderId() { return orderId; }
}

Prefer RuntimeException for business-logic failures. Reserve checked exceptions for truly recoverable I/O errors where the caller has a meaningful choice.

Pattern matching for instanceof in catch (Java 21+)

try {
    ...
} catch (Exception e) {
    String msg = switch (e) {
        case IOException io     -> "IO: " + io.getMessage();
        case SQLException sql   -> "DB " + sql.getSQLState();
        default                 -> "unknown";
    };
}

All sub-topics

Common mistakes

  • Catching Exception or Throwable β€” hides Error, RuntimeException, programming bugs. Catch the narrowest type that makes sense.
  • Empty catch blocks β€” swallows the problem. At minimum, log it.
  • Using exceptions for control flow β€” they're ~100Γ— slower than an if. Validate up front.
  • Wrapping without the cause β€” throw new MyException("failed") loses the stack. Use the two-arg constructor: new MyException("failed", e).

Try it & related tools

The Java Online Compiler shows full stack traces on uncaught exceptions. For structured error payloads in APIs, the JSON to POJO tool generates error DTO classes.