What you'll learn
Things go wrong: files are missing, networks drop, users type nonsense. A robust program anticipates failure and recovers gracefully instead of crashing. Exception handling is how Java lets you separate "what to do" from "what to do when it breaks."
By the end you'll be able to:
- Understand the exception hierarchy and errors vs exceptions
- Tell apart checked and unchecked exceptions
- Handle failures with
try,catch, andfinally - Signal problems with
throwand declare them withthrows - Auto-close resources with try-with-resources
- Create custom exceptions and follow handling best practices
Errors vs exceptions
Everything throwable in Java descends from Throwable, which splits into two branches. Errors are severe, usually-unrecoverable JVM problems (like running out of memory) — you don't catch these. Exceptions are conditions your program can and should handle:
Checked vs unchecked exceptions
Exceptions come in two kinds, and the difference decides whether the compiler forces you to deal with them:
- Checked exceptions (e.g.
IOException,SQLException) represent foreseeable external problems. The compiler requires you to either catch them or declare them withthrows. - Unchecked exceptions (subclasses of
RuntimeException, e.g.NullPointerException,ArithmeticException) usually signal programming bugs. The compiler doesn't force handling — you fix the cause instead.
try, catch & finally
Wrap risky code in a try block. If an exception is thrown, execution jumps to a matching catch block, and the program keeps running instead of crashing:
try {
int result = 10 / 0; // throws ArithmeticException
System.out.println(result); // never reached
} catch (ArithmeticException e) {
System.out.println("Can't divide by zero!");
}
System.out.println("Program continues normally");You can have several catch blocks for different exception types, or combine them with multi-catch (|). A finally block runs no matter what — success or failure — which makes it ideal for cleanup:
try {
int[] arr = {1, 2, 3};
System.out.println(arr[5]); // out of bounds
} catch (ArithmeticException | ArrayIndexOutOfBoundsException e) {
System.out.println("Caught: " + e.getMessage());
} finally {
System.out.println("Finally always runs (cleanup here)");
}throw & throws
Use throw to raise an exception yourself when something is wrong, and throws in a method signature to declare that it might throw a checked exception (passing responsibility to the caller). Don't confuse them: throw is an action; throws is a declaration.
class InsufficientFundsException extends Exception {
InsufficientFundsException(String msg) { super(msg); }
}
public class Bank {
// 'throws' declares this method might fail with that checked exception
static void withdraw(double balance, double amount)
throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException("Not enough money");
}
System.out.println("Withdrew " + amount);
}
public static void main(String[] args) {
try {
withdraw(100, 150);
} catch (InsufficientFundsException e) {
System.out.println("Error: " + e.getMessage());
}
}
}try-with-resources
Resources like files, database connections, and network sockets must be closed, even if an error occurs. Rather than a fiddly finally block, declare the resource in try (...) — Java closes it automatically as soon as the block ends. Any resource that implements AutoCloseable works:
// The resource declared in ( ) is closed automatically —
// even if an exception is thrown. No finally block needed.
try (Scanner sc = new Scanner(new File("data.txt"))) {
System.out.println(sc.nextLine());
} catch (IOException e) {
System.out.println("Could not read the file");
}Tip
finally cleanup — it's shorter, and it can never forget to close or leak a resource.Custom exceptions
When the built-in exceptions don't capture your domain, create your own by extending Exception (checked) or RuntimeException (unchecked), as you saw with InsufficientFundsException above. A well-named custom exception makes error handling self-documenting. You can also chain exceptions — throw new MyException("...", cause) — to preserve the original cause.
Best practices
- Catch specific exceptions, not a blanket
catch (Exception e) - Never swallow exceptions with an empty catch block — at minimum, log them
- Don't use exceptions for normal control flow — they're for exceptional cases
- Clean up with try-with-resources rather than manual code
- Add helpful messages so failures are easy to diagnose
The silent killer
catch { } hides bugs and makes problems impossible to trace. If you truly can't handle an exception, let it propagate — don't bury it.Recap & quick check
Key takeaways
- Throwable splits into Error (don't catch) and Exception (handle).
- Checked exceptions must be caught or declared; unchecked (RuntimeException) needn't be.
- try runs risky code, catch handles failures, finally always runs for cleanup.
- throw raises an exception; throws declares that a method may throw one.
- try-with-resources auto-closes AutoCloseable resources — prefer it over finally.
Quick check
1. Which block always runs, whether or not an exception occurred?
2. What must you do with a checked exception?
3. What's the difference between throw and throws?
4. NullPointerException is an example of a…
5. Why prefer try-with-resources?
Superb — you've completed Phase 2 and can now write robust, well-organised object-oriented Java. Next comes Phase 3 — Core Java, starting with Module 13 on generics.