Lesson 12 โข Expert
Exception Handling
Build robust applications that handle errors gracefully โ because in the real world, things go wrong, and your code needs a plan.
๐ Before You Start
You should be comfortable with:
- Methods (Lesson 6) โ calling and returning values
- OOP (Lesson 9) โ classes, constructors, inheritance
- Control Flow (Lesson 4) โ if/else statements
What You'll Learn
- โ What exceptions are and the exception hierarchy
- โ try-catch-finally blocks and how the flow works
- โ Checked vs unchecked exceptions (and when each applies)
- โ
Throwing exceptions with
throwand declaring withthrows - โ Creating meaningful custom exception classes
- โ Try-with-resources for automatic cleanup (Java 7+)
1๏ธโฃ How Exceptions Work
Real-world analogy: Exceptions are like emergency procedures. When a fire alarm goes off, you don't freeze โ you follow a protocol. Similarly, try-catch lets your program detect problems and respond gracefully instead of crashing.
try {
// Risky code that might fail
int result = 10 / 0;
System.out.println("This never prints!");
} catch (ArithmeticException e) {
// Handle the specific error
System.out.println("Cannot divide by zero: " + e.getMessage());
} finally {
// ALWAYS runs โ use for cleanup
System.out.println("Cleanup complete");
}2๏ธโฃ The Exception Hierarchy
Understanding this tree is essential โ it determines what you must catch vs what's optional:
Throwable
โโโ Error (DON'T catch โ JVM problems)
โ โโโ OutOfMemoryError
โ โโโ StackOverflowError
โโโ Exception
โโโ IOException (CHECKED โ must handle)
โโโ SQLException (CHECKED โ must handle)
โโโ RuntimeException (UNCHECKED โ optional to catch)
โโโ NullPointerException
โโโ ArrayIndexOutOfBoundsException
โโโ ArithmeticException
โโโ IllegalArgumentExceptionChecked exceptions: The compiler forces you to handle them. These represent recoverable problems.
Unchecked exceptions: Extend RuntimeException. Usually indicate programming bugs.
Try It: Basic Exception Handling
See how try-catch-finally controls program flow during errors
// ๐ก Try modifying this code and see what happens!
// Basic Exception Handling (Simulated)
console.log("=== Try-Catch-Finally Flow ===\n");
// 1. Basic flow
console.log("1. Normal flow:");
try {
console.log(" Try block starts");
let result = 10 / 2;
console.log(" Result: " + result);
} catch (error) {
console.log(" Catch: " + error.message);
} finally {
console.log(" Finally runs (always)");
}
// 2. Error flow
console.log("\n2. Error flow:");
try {
console.log(" T
...3๏ธโฃ Throwing & Declaring Exceptions
throw creates and throws an exception. throws declares that a method might throw one.
// Throwing โ validate input and reject bad data
public void setAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("Age must be 0-150, got: " + age);
}
this.age = age;
}
// Custom exception for domain-specific errors
public class InsufficientFundsException extends Exception {
private double deficit;
public InsufficientFundsException(double deficit) {
super("Insufficient funds: need $" + deficit + " more");
this.deficit = deficit;
}
public double getDeficit() { return deficit; }
}4๏ธโฃ Try-With-Resources (Java 7+)
Analogy: It's like a self-closing door โ you walk through, and it automatically closes behind you. No need to remember to close files or connections manually.
// OLD way โ error-prone, verbose
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("data.txt"));
String line = reader.readLine();
} finally {
if (reader != null) reader.close(); // messy!
}
// MODERN way โ clean and safe!
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
String line = reader.readLine();
// reader.close() called AUTOMATICALLY!
} catch (IOException e) {
System.out.println("File error: " + e.getMessage());
}Try It: Custom Exception Classes
Build a bank account system with domain-specific exceptions
// ๐ก Try modifying this code and see what happens!
// Custom Exceptions โ Bank Account (Simulated)
console.log("=== Custom Exceptions ===\n");
class InsufficientFundsError extends Error {
constructor(balance, amount) {
super("Need $" + (amount - balance).toFixed(2) + " more");
this.name = "InsufficientFundsError";
this.balance = balance;
this.attempted = amount;
}
}
class AccountFrozenError extends Error {
constructor(reason) {
super("Accoun
...5๏ธโฃ Common Beginner Mistakes
โ Mistake 1: Catching Exception too broadly
// BAD โ hides ALL errors including bugs!
catch (Exception e) { System.out.println("Something happened"); }
// GOOD โ catch specific exceptions first
catch (FileNotFoundException e) { /* handle missing file */ }
catch (IOException e) { /* handle other I/O issues */ }โ Mistake 2: Empty catch blocks (swallowing exceptions)
// TERRIBLE โ error silently disappears!
catch (Exception e) { }
// At minimum, log it
catch (Exception e) { logger.error("Failed to process", e); }โ Mistake 3: Using exceptions for flow control
Don't use try-catch as an if-else replacement. Exceptions are for exceptional situations โ they're 100x slower than normal conditionals.
6๏ธโฃ Pro Tips
๐ก Catch the most specific exception type first (child before parent).
๐ก Include context in exception messages: what failed and why.
๐ก Use try-with-resources for anything that implements AutoCloseable.
๐ก Create custom exceptions for your domain: InsufficientFundsException is clearer than RuntimeException.
๐ก Log exceptions at the point of handling, not at every layer they pass through.
Try It: Resource Cleanup & Form Validation
Practice try-with-resources patterns and input validation
// ๐ก Try modifying this code and see what happens!
// Resource Cleanup & Validation (Simulated)
console.log("=== Resource Cleanup ===\n");
// Simulating try-with-resources
class DatabaseConnection {
constructor(name) { this.name = name; this.open = true; console.log(" ๐ Opened " + name); }
query(sql) {
if (!this.open) throw new Error("Connection closed!");
console.log(" ๐ Running: " + sql);
return [{id: 1, name: "Alice"}, {id: 2, name: "Bob"}];
}
clo
...๐ Quick Reference
| Keyword | Syntax | Purpose |
|---|---|---|
| try | try { risky code } | Wrap risky code |
| catch | catch (IOException e) | Handle specific error |
| finally | finally { cleanup } | Always runs (cleanup) |
| throw | throw new Exception(msg) | Create & throw error |
| throws | void read() throws IOException | Declare checked exception |
| try-with | try (var r = new R()) | Auto-close resources |
๐ Lesson Complete!
You can now write robust Java code that handles errors gracefully โ from basic try-catch to custom exceptions to try-with-resources for automatic cleanup.
Next up: Collections Framework โ work with ArrayList, HashMap, HashSet, and more powerful data structures.
Sign up for free to track which lessons you've completed and get learning reminders.