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:
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
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:
try (var reader = Files.newBufferedReader(Path.of("data.txt"))) {
System.out.println(reader.readLine());
} // reader.close() happens automaticallyAlways prefer this to a manual finally { reader.close(); } — it's
shorter and handles the awkward case where close() itself throws.
Custom exceptions
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
Optionalfor "might not exist" (next chapter). - Comparing constants first:
"admin".equals(role)never NPEs, even ifroleis null.
Anti-patterns
// 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
What is the difference between `throw` and `throws`?
What does `finally` block do?
What is the difference between checked and unchecked exceptions?
What does `try-with-resources` do?