Exception Handling
Real software encounters unexpected conditions: files that don't exist, invalid user input, network failures, division by zero. Exception handling lets your program respond gracefully instead of crashing.
The Exception Hierarchy
In Java, every exception is an object. The hierarchy matters:
Throwable— root of all throwable types.Error— serious JVM problems (OutOfMemoryError, StackOverflowError) — don't try to catch these.Exception— recoverable problems — these are what you handle.RuntimeException— subclass of Exception — unchecked: NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException.- All other
Exceptionsubclasses — checked: IOException, SQLException — the compiler forces you to handle or declare these.
try / catch / finally
public class FileProcessor {
public static void main(String[] args) {
try {
// Risky code goes here
int[] marks = {65, 72, 80};
System.out.println(marks[5]); // index 5 doesn't exist
} catch (ArrayIndexOutOfBoundsException e) {
// Handles this specific exception type
System.out.println("Error: " + e.getMessage());
System.out.println("Valid indexes are 0 to " + (marks.length - 1));
} finally {
// Runs ALWAYS — whether or not an exception occurred
// Ideal for releasing resources: closing files, DB connections
System.out.println("Processing attempt complete.");
}
}
}
Multiple catch Blocks
Catch different exception types in separate blocks. Order matters — catch more specific types first; a broader type (like Exception) must come after all specific ones.
public static double safeDivide(String numerator, String denominator) {
try {
int n = Integer.parseInt(numerator); // may throw NumberFormatException
int d = Integer.parseInt(denominator); // may throw NumberFormatException
return (double) n / d; // may throw ArithmeticException
} catch (NumberFormatException e) {
System.out.println("Non-numeric input: " + e.getMessage());
return 0.0;
} catch (ArithmeticException e) {
System.out.println("Division by zero.");
return Double.NaN;
} catch (Exception e) {
// Catch-all for anything unexpected — rarely ideal
System.out.println("Unexpected: " + e.getClass().getSimpleName());
return 0.0;
}
}
System.out.println(safeDivide("10", "4")); // 2.5
System.out.println(safeDivide("10", "0")); // Division by zero. NaN
System.out.println(safeDivide("abc", "4")); // Non-numeric input. 0.0
Checked vs Unchecked Exceptions
import java.io.*;
// Checked exception — compiler forces you to handle it
public static String readFirstLine(String filename) throws IOException {
// If you don't handle IOException here, you must declare throws IOException
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
return reader.readLine(); // may throw IOException
}
}
// Unchecked exception — no compile-time requirement, but you should still handle it
public static int getElement(int[] arr, int index) {
if (index < 0 || index >= arr.length) {
// Throw unchecked — caller should validate before calling
throw new IllegalArgumentException(
"Index " + index + " is out of range for array of length " + arr.length);
}
return arr[index];
}
Creating Custom Exceptions
Custom exceptions make your code self-documenting — instead of a generic IllegalArgumentException, throw an InsufficientFundsException that tells the reader exactly what went wrong.
// Custom checked exception
public class InsufficientFundsException extends Exception {
private double shortfall;
public InsufficientFundsException(double shortfall) {
super(String.format("Insufficient funds. Short by R%.2f.", shortfall));
this.shortfall = shortfall;
}
public double getShortfall() { return shortfall; }
}
// Using it in BankAccount:
public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException(amount - balance);
}
balance -= amount;
}
// Handling it:
try {
account.withdraw(5000.00);
} catch (InsufficientFundsException e) {
System.out.println(e.getMessage());
System.out.printf("You need an additional R%.2f.%n", e.getShortfall());
}
try-with-resources
Ensures resources (files, database connections, network sockets) are automatically closed even if an exception occurs. Any class implementing AutoCloseable can be used.
// Without try-with-resources (risky — close might not be called on exception):
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("data.txt"));
System.out.println(reader.readLine());
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
} finally {
if (reader != null) try { reader.close(); } catch (IOException e) { }
}
// With try-with-resources (clean — close is always called):
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
System.out.println(reader.readLine());
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
}try-with-resources
Practice Task
Your Turn
Write a LearnerDatabase class that stores learners in an array. Add methods: addLearner(Learner l) throws DatabaseFullException (custom) if the array is full, getLearner(int index) throws LearnerNotFoundException (custom) if the index is invalid, updateMark(int index, double mark) throws InvalidMarkException if mark is outside 0-100. Test in main with all valid and invalid cases, catching each exception and printing a meaningful message.
Common Mistakes
- Empty catch blocks — catching and doing nothing hides bugs completely. At minimum, log the exception.
- Catching
Exceptioneverywhere — catch the most specific type you can actually handle. - Using exceptions for normal flow control — exceptions are for exceptional conditions, not expected logic paths.
- Forgetting finally for resource cleanup — use try-with-resources for anything that needs closing.
- Throwing checked exceptions where unchecked would be more appropriate — if the caller cannot reasonably recover, use RuntimeException or a subclass.
Professional Tip
Print the stack trace during development: e.printStackTrace() shows you exactly where the exception was thrown and the call chain leading to it. In production, log it properly instead of printing to the console.
Mini Quiz
What is the difference between checked and unchecked exceptions?