Java Exceptions

Java
try {
    int result = 10 / 0;
} catch (ArithmeticException e) {
    System.out.println("Error: " + e.getMessage());
} finally {
    System.out.println("Always runs");
}

Checked vs unchecked

Checked Unchecked
Extends Exception RuntimeException
Compiler forces handling yes no
Examples IOException, SQLException NullPointerException, IllegalArgumentException

Checked exceptions must be caught or declared:

Java
public void readFile() throws IOException {
    Files.readString(Path.of("data.txt"));
}

Rule of thumb: use unchecked for programming errors (bad arguments, broken invariants) and checked only when the caller can realistically recover.

Catching multiple types

Java
try {
    riskyOperation();
} catch (IOException | SQLException e) {
    log(e);
} catch (Exception e) {
    // anything else
}

Order matters — a broader type must come after narrower ones, or the code won't compile.

try-with-resources

Anything implementing AutoCloseable is closed automatically, even if an exception is thrown:

Java
try (var reader = Files.newBufferedReader(Path.of("data.txt"))) {
    System.out.println(reader.readLine());
}   // reader.close() happens automatically

Always prefer this to a manual finally { reader.close(); } — it's shorter and handles the awkward case where close() itself throws.

Custom exceptions

Java
public class OutOfStockException extends RuntimeException {
    public OutOfStockException(String message) {
        super(message);
    }
}

if (stock < qty) {
    throw new OutOfStockException("Only " + stock + " left");
}

NullPointerException

The most common Java exception. Java 14+ gives helpful messages telling you exactly which reference was null.

Avoid it by:

  • Returning empty collections instead of null.
  • Using Optional for "might not exist" (next chapter).
  • Comparing constants first: "admin".equals(role) never NPEs, even if role is null.

Anti-patterns

Java
// swallowing — the bug vanishes silently
try { risky(); } catch (Exception e) { }

// catching Throwable — also catches OutOfMemoryError
try { risky(); } catch (Throwable t) { }

// using exceptions for normal control flow
try { return list.get(i); } catch (IndexOutOfBoundsException e) { return null; }

Catch what you can actually handle; let the rest travel up to a layer that logs it properly.

Knowledge Check

4 questions — test your understanding

1

What is the difference between `throw` and `throws`?

2

What does `finally` block do?

3

What is the difference between checked and unchecked exceptions?

4

What does `try-with-resources` do?