Day 14: Exception Handling
An exception is an unexpected situation that occurs during program execution. Exception handling prevents the program from abruptly terminating and allows appropriate recovery logic to execute. Java treats exceptions as objects and provides a rich exception hierarchy.
try-catch-finally Basics
The basic structure for catching and handling exceptions.
public class ExceptionBasic {
public static void main(String[] args) {
// Basic try-catch
try {
int result = 10 / 0;
System.out.println("Result: " + result); // Not executed
} catch (ArithmeticException e) {
System.out.println("Cannot divide by zero: " + e.getMessage());
}
// Handling multiple exceptions
try {
String text = null;
// text.length(); // NullPointerException
int[] arr = {1, 2, 3};
System.out.println(arr[5]); // ArrayIndexOutOfBoundsException
} catch (NullPointerException e) {
System.out.println("Null reference error: " + e.getMessage());
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Array index out of bounds: " + e.getMessage());
} catch (Exception e) {
System.out.println("Other error: " + e.getMessage());
} finally {
// Always executes regardless of whether an exception occurred
System.out.println("Cleanup executed (finally)");
}
// Multi-catch (Java 7+)
try {
String numStr = "abc";
int num = Integer.parseInt(numStr);
} catch (IllegalArgumentException e) {
System.out.println("Conversion error: " + e.getMessage());
}
System.out.println("Program continues running");
}
}
try-with-resources (Automatic Resource Release)
Automatically closes resources that implement AutoCloseable.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class TryWithResources {
// Custom resource
static class DatabaseConnection implements AutoCloseable {
String name;
DatabaseConnection(String name) {
this.name = name;
System.out.println(name + " connection opened");
}
void query(String sql) {
System.out.println(name + " executing query: " + sql);
}
@Override
public void close() {
System.out.println(name + " connection closed");
}
}
public static void main(String[] args) {
// try-with-resources: close() is called automatically
try (DatabaseConnection db = new DatabaseConnection("MySQL")) {
db.query("SELECT * FROM users");
// db.close() is called automatically
}
// Managing multiple resources
try (
DatabaseConnection db1 = new DatabaseConnection("Primary");
DatabaseConnection db2 = new DatabaseConnection("Secondary")
) {
db1.query("INSERT INTO logs VALUES (...)");
db2.query("SELECT * FROM cache");
}
// Closed in reverse order: db2 first, then db1
System.out.println("All resources cleaned up");
}
}
Checked vs Unchecked Exceptions
Understanding the Java exception hierarchy and the difference between two types of exceptions.
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
public class CheckedUnchecked {
// Checked exception: compiler enforces handling
// IOException, SQLException, FileNotFoundException, etc.
static String readFile(String path) throws FileNotFoundException {
File file = new File(path);
if (!file.exists()) {
throw new FileNotFoundException("File not found: " + path);
}
return "File content";
}
// Unchecked exception (RuntimeException): handling not enforced
// NullPointerException, IllegalArgumentException, etc.
static int divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Divisor cannot be zero.");
}
return a / b;
}
static void validateAge(int age) {
if (age < 0 || age > 150) {
throw new IllegalArgumentException("Invalid age: " + age);
}
System.out.println("Valid age: " + age);
}
public static void main(String[] args) {
// Checked: must handle with try-catch or throws
try {
String content = readFile("nonexistent.txt");
} catch (FileNotFoundException e) {
System.out.println("Checked exception: " + e.getMessage());
}
// Unchecked: no compile error even without handling (but recommended)
try {
int result = divide(10, 0);
} catch (IllegalArgumentException e) {
System.out.println("Unchecked exception: " + e.getMessage());
}
validateAge(25);
// validateAge(-5); // IllegalArgumentException thrown
}
}
Custom Exceptions
Create domain-specific exceptions for meaningful error handling.
// Custom checked exception
class InsufficientBalanceException extends Exception {
private final long currentBalance;
private final long requestedAmount;
InsufficientBalanceException(long currentBalance, long requestedAmount) {
super(String.format("Insufficient balance: current %,d, requested %,d",
currentBalance, requestedAmount));
this.currentBalance = currentBalance;
this.requestedAmount = requestedAmount;
}
public long getShortfall() {
return requestedAmount - currentBalance;
}
}
// Custom unchecked exception
class InvalidAccountException extends RuntimeException {
InvalidAccountException(String accountNumber) {
super("Invalid account number: " + accountNumber);
}
}
class BankService {
private long balance;
BankService(long initialBalance) {
this.balance = initialBalance;
}
void withdraw(long amount) throws InsufficientBalanceException {
if (amount <= 0) {
throw new IllegalArgumentException("Withdrawal amount must be positive.");
}
if (amount > balance) {
throw new InsufficientBalanceException(balance, amount);
}
balance -= amount;
System.out.println(String.format("%,d withdrawn. Balance: %,d", amount, balance));
}
long getBalance() {
return balance;
}
}
public class CustomExceptionExample {
public static void main(String[] args) {
BankService bank = new BankService(100000);
try {
bank.withdraw(50000); // Success
bank.withdraw(80000); // Insufficient balance
} catch (InsufficientBalanceException e) {
System.out.println(e.getMessage());
System.out.println("Shortfall: " + String.format("%,d", e.getShortfall()));
}
try {
bank.withdraw(-1000); // Invalid amount
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
}
Today’s Exercises
-
Input Validator: Create a
Validatorclass that validates username, email, and password. Throw a customValidationExceptionfor each validation failure, and collect multiple validation errors to report them all at once. -
File Processor: Write a program that uses try-with-resources to open a resource (custom
Connectionclass), and verify that the resource is safely closed even if an exception occurs during processing. -
Exception Chaining: Implement a 3-level exception chain where
DataAccessExceptionwrapsSQLException, andServiceExceptionwrapsDataAccessException. Trace the root cause usinggetCause().