Lesson 08: Exception Handling
Master Java exception handling to write robust, error-resistant applications that gracefully handle unexpected situations.
Introduction
In real-world applications, things go wrong: files might not exist, network connections can fail, users enter invalid data, or calculations result in division by zero. Without proper exception handling, these problems would crash your program and frustrate users. Exception handling in Java provides a structured way to detect, handle, and recover from errors gracefully. Instead of your application suddenly stopping with a cryptic error message, you can catch problems, provide meaningful feedback, clean up resources, and keep your program running smoothly. This lesson teaches you how to use try-catch blocks, create custom exceptions, handle resources safely, and follow best practices that make your code resilient and professional.
Understanding Exceptions
Definition
An exception is an event that occurs during program execution that disrupts the normal flow of instructions. When an error condition arises, Java creates an exception object containing information about the error and "throws" it up the call stack. Exceptions can be checked (must be handled) or unchecked (runtime exceptions that indicate programming errors).
Analogy
Think of exceptions like fire alarms in a building. When something goes wrong (smoke detected), the alarm system alerts everyone, indicates where it happened (stack trace), and activates emergency procedures (exception handling). Some alarms require immediate action (checked exceptions), while others indicate user errors like burnt toast (unchecked exceptions).
Examples
Division by zero:
int result = 10 / 0; // throws ArithmeticException
Invalid array access:
int[] numbers = {1, 2, 3};
int value = numbers[5]; // throws ArrayIndexOutOfBoundsException
Checked vs Unchecked:
new FileReader("data.txt"); // checked: IOException
List list = List.of("a", "b");
list.get(5); // unchecked: IndexOutOfBoundsException
try-catch-finally
Definition
The try-catch-finally structure handles exceptions gracefully. The try block contains code that might throw; catch handles specific types; finally always executes for cleanup.
Analogy
Like setting up camp: try to light a fire, catch problems with backup plans, and finally put food away and secure gear no matter what.
Examples
Basic try-catch:
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Cannot divide by zero!");
}
Multiple catch (specific to general):
try {
String content = Files.readString(Path.of(fileName));
} catch (NoSuchFileException e) {
System.out.println("File not found: " + fileName);
} catch (IOException e) {
System.out.println("Error reading file: " + e.getMessage());
}
finally for cleanup:
FileInputStream in = null;
try {
in = new FileInputStream("data.txt");
// process
} catch (IOException e) {
System.out.println("File error: " + e.getMessage());
} finally {
if (in != null) in.close();
}
Throwing Exceptions
Definition
Use throw to signal errors and throws to declare checked exceptions that callers must handle. This creates a clear error contract.
Analogy
Like a quality inspector stopping a production line and escalating with a report so supervisors decide what to do.
Examples
Validate and throw:
if (age < 0) throw new IllegalArgumentException("Age cannot be negative");
Method declares throws:
public void readFile(String name) throws IOException {
Files.readString(Path.of(name));
}
Caller handles declared exception:
try {
readFile("data.txt");
} catch (IOException e) {
System.out.println("Failed to read file");
}
Rethrow with context:
try {
processOrder(order);
} catch (Exception e) {
throw new IllegalStateException("Failed to process order " + order.getId(), e);
}
Custom Exceptions
Definition
Create domain-specific exception types to make errors clearer. Extend RuntimeException (unchecked) or Exception (checked).
Analogy
Like departments using specific error codes (InvalidAddress, OutOfStock) so issues are obvious and routed correctly.
Examples
Simple custom exception:
class InvalidEmailException extends RuntimeException {
public InvalidEmailException(String msg) { super(msg); }
}
Throw and catch:
if (!email.contains("@")) throw new InvalidEmailException("Email must contain @");
try { validateUser(user); } catch (InvalidEmailException e) { showEmailError(e.getMessage()); }
Custom with extra data:
class InsufficientFundsException extends RuntimeException {
final double requested, available;
public InsufficientFundsException(double r, double a){
super("Insufficient funds: requested " + r + ", available " + a);
this.requested = r; this.available = a;
}
}
Try-with-Resources
Definition
try-with-resources automatically closes resources that implement AutoCloseable, preventing leaks without verbose finally blocks.
Analogy
Like smart lights that always turn off when you leave the room, even during emergencies.
Examples
Read file safely:
try (BufferedReader r = Files.newBufferedReader(path)) {
return r.readLine();
}
Database query:
try (Connection c = DriverManager.getConnection(url);
Statement s = c.createStatement()) {
ResultSet rs = s.executeQuery("SELECT * FROM users");
return processResults(rs);
}
Custom AutoCloseable:
class TimedOperation implements AutoCloseable {
long start = System.currentTimeMillis();
public void close(){
System.out.println("Operation took: " + (System.currentTimeMillis()-start) + "ms");
}
}
Exception Handling Best Practices
Definition
Write code that fails gracefully, communicates clearly, and cleans up safely. Catch specific types, avoid empty catches, use try-with-resources, fail fast, and don't use exceptions for normal control flow.
Analogy
Like hospital emergency protocols: specific responses for each situation, documentation, no ignored emergencies, and automatic maintenance.
Examples
Catch specific exceptions first:
try {
processFile(fileName);
} catch (FileNotFoundException e) {
createDefaultFile(fileName);
} catch (IOException e) {
logError("File processing failed", e);
}
Never ignore exceptions:
try { saveImportantData(); } catch (IOException e) {
logger.error("Failed to save data", e);
throw new RuntimeException("Data save failed", e);
}
Don't use exceptions for control flow:
if (index >= 0 && index < array.length) return array[index];
else return defaultValue;
Summary
Exception handling is essential for building robust Java applications that gracefully handle errors and unexpected situations. You've learned to use try-catch-finally blocks to handle exceptions, throw your own exceptions with meaningful messages, create custom exception types for specific business errors, and use try-with-resources for automatic cleanup. Following best practices like catching specific exceptions, providing helpful error messages, and failing fast on programming errors will make your applications more reliable and easier to debug. In the next lesson, you'll apply these exception handling skills when working with File I/O operations, where proper error handling is crucial for dealing with missing files, permission issues, and other I/O problems.
Programming Challenge
Challenge: Robust File Processor
Task: Create a file processing system that reads student data from a CSV file and handles various error conditions gracefully.
Requirements:
- Create a custom exception
StudentDataException
that includes the line number and invalid data - Create a
Student
class with: name, age, grade (A-F), and email - Write a method
processStudentFile(String filename)
that: - Uses try-with-resources to read the CSV file safely
- Validates each student record and throws
StudentDataException
for invalid data - Handles
FileNotFoundException
andIOException
appropriately - Returns a list of valid students and logs errors for invalid records
- Create a method that demonstrates both handling and propagating exceptions
- Include proper error messages with context (line numbers, invalid values)
Test scenarios:
- Missing file
- Invalid age (negative or non-numeric)
- Invalid grade (not A-F)
- Invalid email format
- Empty or malformed lines
Learning Goals: Practice custom exceptions, try-with-resources, exception propagation, and comprehensive error handling in a realistic file processing scenario.