Exception Handling
Handle runtime errors gracefully with try-catch-finally, throw exceptions, and understand checked vs unchecked exceptions.
Overview
Key Points
- •try block: code that might throw an exception
- •catch (ExceptionType e): catches and handles that type of exception
- •finally: always runs - use for cleanup (close files, connections)
- •throw: manually throw an exception
- •Checked exceptions (IOException, SQLException): must be caught or declared with throws
- •Unchecked exceptions (RuntimeException subclasses): ArithmeticException, NullPointerException, ArrayIndexOutOfBoundsException - optional to catch
- •catch blocks are matched in order - put specific types before general ones
- •Exception message: e.getMessage(), stack trace: e.printStackTrace()
Examples
Basic try-catch and finally
public class Main {
public static void main(String[] args) {
// ArithmeticException: division by zero
try {
int result = 10 / 0;
System.out.println(result); // never reached
} catch (ArithmeticException e) {
System.out.println("Cannot divide by zero: " + e.getMessage());
} finally {
System.out.println("This always runs.");
}
// NullPointerException
try {
String s = null;
System.out.println(s.length());
} catch (NullPointerException e) {
System.out.println("Null reference: " + e.getMessage());
}
System.out.println("Program continues normally.");
}
}finally runs whether or not an exception was caught. Code after the try-catch block continues normally after a caught exception.
Multiple catch blocks and catching multiple types
public class Main {
static int divide(int a, int b) {
return a / b; // throws ArithmeticException if b=0
}
static int getElement(int[] arr, int index) {
return arr[index]; // throws ArrayIndexOutOfBoundsException if out of range
}
public static void main(String[] args) {
int[] numbers = {10, 20, 30};
try {
System.out.println(divide(10, 0));
} catch (ArithmeticException e) {
System.out.println("Math error: " + e.getMessage());
}
try {
System.out.println(getElement(numbers, 10));
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Index out of range: " + e.getMessage());
}
// Catching multiple in one block (Java 7+)
try {
String s = null;
s.length();
} catch (NullPointerException | IllegalArgumentException e) {
System.out.println("Error: " + e.getMessage());
}
}
}Specific exception types before general ones. `catch (TypeA | TypeB e)` catches either type in one block.
Throwing exceptions and custom validation
class BankAccount {
private double balance;
BankAccount(double initial) {
if (initial < 0) {
throw new IllegalArgumentException("Initial balance cannot be negative: " + initial);
}
this.balance = initial;
}
void withdraw(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Withdrawal amount must be positive.");
}
if (amount > balance) {
throw new ArithmeticException("Insufficient funds. Balance: " + balance);
}
balance -= amount;
System.out.printf("Withdrew $%.2f. Balance: $%.2f%n", amount, balance);
}
double getBalance() { return balance; }
}
public class Main {
public static void main(String[] args) {
try {
BankAccount acc = new BankAccount(100.0);
acc.withdraw(30.0);
acc.withdraw(200.0); // throws
} catch (ArithmeticException e) {
System.out.println("Transaction failed: " + e.getMessage());
} catch (IllegalArgumentException e) {
System.out.println("Invalid input: " + e.getMessage());
}
}
}throw creates and throws an exception immediately. Execution jumps out of the current method and up the call stack until a matching catch is found.
Exception hierarchy - catching with a supertype
public class Main {
public static void main(String[] args) {
try {
String s = null;
s.length(); // NullPointerException
} catch (RuntimeException e) {
// Catches NullPointerException (and any other RuntimeException)
System.out.println("Runtime error: " + e.getClass().getSimpleName());
System.out.println("Message: " + e.getMessage());
}
// Exception is the supertype of all exceptions
try {
int[] arr = new int[5];
arr[10] = 1;
} catch (Exception e) {
System.out.println("Caught: " + e.getClass().getSimpleName());
}
}
}Catching a supertype (Exception) catches all subtypes. Prefer specific types when possible so you handle each case appropriately.
Common Mistakes
- Catching Exception (the broadest type) everywhere - you lose information about what actually went wrong and may silently swallow bugs
- Empty catch blocks: `catch (Exception e) {}` - the error is silently ignored, making bugs very hard to find
- Putting too much code in try blocks - keep try blocks small, covering only the lines that might throw
- Putting specific catch blocks after general ones - `catch (Exception e)` before `catch (NullPointerException e)` makes the specific one unreachable
Note
Handle exceptions at the level where you can do something meaningful about them. If you can't handle it, let it propagate up. Always log or print the exception details - a silent catch is almost always wrong.
Exercises
Write Java code and click Run to execute it instantly. When your output looks correct, mark as done.
Completed: 0 / 4 exercises
Knowledge Check
Question 1 of 4