Notifications

No notifications

/Phase 3

Exceptions & Error Handling

An exception is Java's way of saying "something went wrong, here's a typed object describing what." You handle it with try/catch, clean up with finally (or better, try-with-resources), and propagate with throws. Java is famous (and infamous) for splitting exceptions into *checked* (must be handled) and *unchecked* (RuntimeException family).

On this page

Detailed Theory

# Exceptions

The hierarchy

Throwable
├── Error          ← JVM-level (OutOfMemoryError) — don't catch
└── Exception
    ├── RuntimeException   ← UNCHECKED  (NullPointerException, IllegalArgumentException, …)
    └── (everything else)  ← CHECKED   (IOException, SQLException, …)

  • Checked: compiler forces you to either catch it or declare it with throws.
  • Unchecked: compiler is silent — you can let it bubble up.

try / catch / finally

try {
    risky();
} catch (FileNotFoundException e) {
    System.err.println("missing: " + e.getMessage());
} catch (IOException e) {
    System.err.println("io: " + e.getMessage());
} finally {
    System.out.println("always runs");
}
Order matters — more specific exceptions go first.

Multi-catch (Java 7+)

try { … }
catch (IOException | SQLException e) {       // pipe-separated
    log(e);
}

try-with-resources (Java 7+) — preferred for I/O

Anything that implements AutoCloseable is auto-closed in reverse order, even if an exception is thrown.
try (var br = new BufferedReader(new FileReader("data.txt"))) {
    return br.readLine();
}     // br.close() called automatically

throw vs throws

  • throw new X() — *raise* an exception now.
  • throws X (in method signature) — *declare* this method may raise X.
public static int parse(String s) throws NumberFormatException {
    if (s == null) throw new IllegalArgumentException("s is null");
    return Integer.parseInt(s);
}

Custom exceptions

public class InsufficientFundsException extends RuntimeException {
    private final double shortfall;
    public InsufficientFundsException(double shortfall) {
        super("short by $" + shortfall);
        this.shortfall = shortfall;
    }
    public double getShortfall() { return shortfall; }
}
Extend RuntimeException for unchecked, plain Exception for checked.

Exception chaining

try { db.query(); }
catch (SQLException e) {
    throw new ServiceException("user lookup failed", e);   // wrap, keep cause
}
Use e.getCause() later to recover the original.