Java Programming Language
Chapter 9: Exception Handling
Managing Runtime Errors Gracefully
Course: 4343203 - Java Programming
What We'll Cover
- Exception Fundamentals
- Exception Hierarchy
- try-catch-finally Blocks
- Types of Exceptions
- Throwing Exceptions
- Custom Exceptions
- Exception Propagation
- Best Practices
Exception Hierarchy
Exception Fundamentals
Exception is an event that disrupts normal program flow
What are Exceptions?
Exception Characteristics:
- Runtime errors that can be handled
- Objects that describe error conditions
- Disrupt normal program execution
- Can be caught and processed
- Provide error information and stack trace
Without Exception Handling:
public class WithoutExceptionHandling {
public static void main(String[] args) {
int[] numbers = {1, 2, 3};
// This will crash the program
System.out.println(numbers[5]);
// This line will never execute
System.out.println("Program continues...");
}
}
// Output:
// Exception in thread "main"
// java.lang.ArrayIndexOutOfBoundsException:
// Index 5 out of bounds for length 3
With Exception Handling:
public class WithExceptionHandling {
public static void main(String[] args) {
int[] numbers = {1, 2, 3};
try {
System.out.println(numbers[5]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Error: Array index out of bounds");
System.out.println("Message: " + e.getMessage());
}
// This line WILL execute
System.out.println("Program continues gracefully...");
}
}
// Output:
// Error: Array index out of bounds
// Message: Index 5 out of bounds for length 3
// Program continues gracefully...
Benefits of Exception Handling
Program Stability
- Prevents crashes
- Graceful degradation
- Continues execution
- User-friendly experience
Error Information
- Detailed error messages
- Stack trace information
- Exception type identification
- Debugging assistance
Separation of Concerns
- Normal code vs error handling
- Clean program structure
- Centralized error handling
- Maintainable code
Types of Exceptions
Java exceptions are categorized into Checked and Unchecked exceptions
Checked vs Unchecked Exceptions
Checked Exceptions
- Must be handled or declared
- Compile-time checking
- Extend Exception class
- Recoverable errors
Examples:
- IOException
- SQLException
- ClassNotFoundException
- FileNotFoundException
Unchecked Exceptions
- Optional handling
- Runtime checking
- Extend RuntimeException
- Programming errors
Examples:
- NullPointerException
- ArrayIndexOutOfBoundsException
- IllegalArgumentException
- NumberFormatException
Common Exception Examples
Runtime Exceptions:
// NullPointerException
String str = null;
// int length = str.length(); // NPE
// ArrayIndexOutOfBoundsException
int[] arr = {1, 2, 3};
// int value = arr[5]; // AIOOBE
// NumberFormatException
// int num = Integer.parseInt("abc"); // NFE
// IllegalArgumentException
public void setAge(int age) {
if (age < 0) {
throw new IllegalArgumentException(
"Age cannot be negative");
}
this.age = age;
}
// ArithmeticException
// int result = 10 / 0; // Division by zero
Checked Exceptions:
// IOException - File operations
try {
FileReader file = new FileReader("data.txt");
BufferedReader reader = new BufferedReader(file);
String line = reader.readLine();
reader.close();
} catch (IOException e) {
System.out.println("File error: " + e.getMessage());
}
// SQLException - Database operations
try {
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost/test", "user", "pass");
// Database operations
} catch (SQLException e) {
System.out.println("Database error: " + e.getMessage());
}
// ClassNotFoundException
try {
Class clazz = Class.forName("com.example.MyClass");
} catch (ClassNotFoundException e) {
System.out.println("Class not found: " + e.getMessage());
}
try-catch-finally Blocks
try-catch-finally provides structured exception handling
Basic try-catch Structure
Single catch block:
public class BasicTryCatch {
public static void main(String[] args) {
try {
int[] numbers = {1, 2, 3};
System.out.println(numbers[5]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Array index error!");
System.out.println("Error message: " + e.getMessage());
e.printStackTrace(); // Print stack trace
}
System.out.println("Program continues...");
}
}
Multiple catch blocks:
try {
String input = getUserInput();
int number = Integer.parseInt(input);
int result = 100 / number;
System.out.println("Result: " + result);
} catch (NumberFormatException e) {
System.out.println("Invalid number format");
} catch (ArithmeticException e) {
System.out.println("Cannot divide by zero");
} catch (Exception e) {
System.out.println("Unexpected error: " + e.getMessage());
}
Multi-catch (Java 7+):
try {
// Risky operations
performFileOperations();
performNetworkOperations();
} catch (FileNotFoundException | IOException e) {
System.out.println("File or IO error: " + e.getMessage());
logError(e);
} catch (SQLException | DataAccessException e) {
System.out.println("Database error: " + e.getMessage());
rollbackTransaction();
}
try-with-resources (Java 7+):
// Automatic resource management
try (FileReader file = new FileReader("data.txt");
BufferedReader reader = new BufferedReader(file)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// Resources automatically closed
} catch (IOException e) {
System.out.println("File error: " + e.getMessage());
}
finally Block
finally Block Characteristics:
- Always executes (except System.exit())
- Runs whether exception occurs or not
- Used for cleanup operations
- Cannot prevent exception propagation
finally Example:
public class FinallyExample {
public static void processFile(String filename) {
FileInputStream file = null;
try {
file = new FileInputStream(filename);
// Process file
int data = file.read();
System.out.println("File processed successfully");
} catch (IOException e) {
System.out.println("Error reading file: " + e.getMessage());
} finally {
// Cleanup - always executes
if (file != null) {
try {
file.close();
System.out.println("File closed");
} catch (IOException e) {
System.out.println("Error closing file");
}
}
}
}
public static void main(String[] args) {
processFile("test.txt");
processFile("nonexistent.txt");
}
}
Execution Flow:
// Case 1: No exception
try {
System.out.println("Normal execution");
return; // finally still executes
} finally {
System.out.println("Cleanup code");
}
// Case 2: Exception caught
try {
throw new RuntimeException("Error");
} catch (RuntimeException e) {
System.out.println("Exception handled");
} finally {
System.out.println("Cleanup code");
}
// Case 3: Exception not caught
try {
throw new IOException("IO Error");
} catch (RuntimeException e) {
System.out.println("Won't catch IOException");
} finally {
System.out.println("Cleanup code"); // Still executes
// Then IOException propagates
}
Throwing Exceptions
throw and throws keywords control exception flow
throw vs throws
throw keyword
- Explicitly throws an exception
- Used inside method body
- Followed by exception object
- Can throw only one exception
Example:
public class ThrowExample {
public static void validateAge(int age) {
if (age < 0) {
throw new IllegalArgumentException(
"Age cannot be negative: " + age);
}
if (age > 150) {
throw new IllegalArgumentException(
"Age seems unrealistic: " + age);
}
System.out.println("Valid age: " + age);
}
public static void main(String[] args) {
try {
validateAge(25); // Valid
validateAge(-5); // Throws exception
} catch (IllegalArgumentException e) {
System.out.println("Invalid age: " + e.getMessage());
}
}
}
throws keyword
- Declares possible exceptions
- Used in method signature
- Followed by exception class names
- Can declare multiple exceptions
Example:
public class ThrowsExample {
// Method declares it may throw IOException
public static void readFile(String filename)
throws IOException, FileNotFoundException {
FileReader file = new FileReader(filename);
BufferedReader reader = new BufferedReader(file);
String line = reader.readLine();
System.out.println("First line: " + line);
reader.close();
}
public static void main(String[] args) {
try {
readFile("data.txt");
} catch (FileNotFoundException e) {
System.out.println("File not found: " + e.getMessage());
} catch (IOException e) {
System.out.println("IO error: " + e.getMessage());
}
}
}
Method Exception Handling Options
import java.io.*;
public class ExceptionHandlingOptions {
// Option 1: Handle the exception within the method
public static void option1_HandleInMethod(String filename) {
try {
FileReader file = new FileReader(filename);
// Process file
file.close();
} catch (IOException e) {
System.out.println("Error in method: " + e.getMessage());
}
}
// Option 2: Declare and let caller handle
public static void option2_DeclareException(String filename) throws IOException {
FileReader file = new FileReader(filename);
// Process file
file.close();
}
// Option 3: Wrap in runtime exception
public static void option3_WrapException(String filename) {
try {
FileReader file = new FileReader(filename);
// Process file
file.close();
} catch (IOException e) {
throw new RuntimeException("Failed to process file: " + filename, e);
}
}
public static void main(String[] args) {
// Option 1: No try-catch needed in caller
option1_HandleInMethod("test.txt");
// Option 2: Caller must handle
try {
option2_DeclareException("test.txt");
} catch (IOException e) {
System.out.println("Caller handling: " + e.getMessage());
}
// Option 3: Runtime exception - optional handling
try {
option3_WrapException("test.txt");
} catch (RuntimeException e) {
System.out.println("Runtime exception: " + e.getMessage());
System.out.println("Caused by: " + e.getCause());
}
}
}
Custom Exceptions
Custom exceptions provide application-specific error handling
Creating Custom Exceptions
Custom Checked Exception:
// Custom checked exception
public class InsufficientFundsException extends Exception {
private double amount;
private double balance;
public InsufficientFundsException(double amount, double balance) {
super("Insufficient funds: Attempted to withdraw $" +
amount + " but balance is only $" + balance);
this.amount = amount;
this.balance = balance;
}
public double getAmount() { return amount; }
public double getBalance() { return balance; }
public double getShortfall() { return amount - balance; }
}
// Usage in BankAccount class
public class BankAccount {
private double balance;
public BankAccount(double initialBalance) {
this.balance = initialBalance;
}
public void withdraw(double amount)
throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException(amount, balance);
}
balance -= amount;
}
public double getBalance() { return balance; }
}
Custom Unchecked Exception:
// Custom unchecked exception
public class InvalidEmailException extends RuntimeException {
private String email;
public InvalidEmailException(String email) {
super("Invalid email format: " + email);
this.email = email;
}
public InvalidEmailException(String email, Throwable cause) {
super("Invalid email format: " + email, cause);
this.email = email;
}
public String getEmail() { return email; }
}
// Usage in User class
public class User {
private String email;
public void setEmail(String email) {
if (!isValidEmail(email)) {
throw new InvalidEmailException(email);
}
this.email = email;
}
private boolean isValidEmail(String email) {
return email != null &&
email.contains("@") &&
email.contains(".");
}
}
Using Custom Exceptions
public class CustomExceptionDemo {
public static void main(String[] args) {
// Test custom checked exception
BankAccount account = new BankAccount(1000.0);
try {
account.withdraw(500.0); // Success
System.out.println("Withdrawal successful. Balance: $" + account.getBalance());
account.withdraw(600.0); // Will throw exception
} catch (InsufficientFundsException e) {
System.out.println("Transaction failed: " + e.getMessage());
System.out.println("Shortfall: $" + e.getShortfall());
}
// Test custom unchecked exception
User user = new User();
try {
user.setEmail("valid@email.com"); // Success
System.out.println("Email set successfully");
user.setEmail("invalid-email"); // Will throw exception
} catch (InvalidEmailException e) {
System.out.println("Email validation failed: " + e.getMessage());
System.out.println("Invalid email was: " + e.getEmail());
}
}
}
Exception Propagation
Exception propagation follows the call stack upward until caught
How Exceptions Propagate
public class ExceptionPropagation {
public static void method1() {
System.out.println("Entering method1");
method2();
System.out.println("Exiting method1"); // Won't execute if exception
}
public static void method2() {
System.out.println("Entering method2");
method3();
System.out.println("Exiting method2"); // Won't execute if exception
}
public static void method3() {
System.out.println("Entering method3");
// This will throw an exception
int result = 10 / 0; // ArithmeticException
System.out.println("Exiting method3"); // Won't execute
}
public static void main(String[] args) {
try {
System.out.println("Starting main");
method1();
System.out.println("Finishing main"); // Won't execute if exception
} catch (ArithmeticException e) {
System.out.println("Caught in main: " + e.getMessage());
e.printStackTrace();
}
System.out.println("Program continues after exception handling");
}
}
// Output:
// Starting main
// Entering method1
// Entering method2
// Entering method3
// Caught in main: / by zero
// java.lang.ArithmeticException: / by zero
// at ExceptionPropagation.method3(...)
// at ExceptionPropagation.method2(...)
// at ExceptionPropagation.method1(...)
// at ExceptionPropagation.main(...)
// Program continues after exception handling
Exception Handling Best Practices
Best Practices
✅ Do's
- Catch specific exceptions first
- Use meaningful error messages
- Log exceptions appropriately
- Clean up resources in finally
- Use try-with-resources when possible
- Create custom exceptions for domain logic
- Include relevant context in exceptions
❌ Don'ts
- Don't catch and ignore exceptions
- Don't catch Exception or Throwable
- Don't use exceptions for control flow
- Don't throw exceptions in finally
- Don't return from finally block
- Don't print stack traces in production
- Don't create unnecessary custom exceptions
Exception Handling Examples
❌ Poor Exception Handling:
// Don't do this!
try {
// Some operation
performRiskyOperation();
} catch (Exception e) {
// Silently ignoring exception
}
// Or this!
try {
// Some operation
performAnotherOperation();
} catch (Exception e) {
e.printStackTrace(); // Just printing
}
// Or this!
public void badMethod() throws Exception {
// Too generic
throw new Exception("Something went wrong");
}
✅ Good Exception Handling:
// Do this instead!
try {
performRiskyOperation();
} catch (SpecificException e) {
logger.error("Operation failed: " + e.getMessage(), e);
// Take appropriate action
notifyUser("Operation failed, please try again");
} catch (AnotherSpecificException e) {
logger.warn("Minor issue: " + e.getMessage());
// Handle differently
useAlternativeApproach();
}
// Good custom exception
public void goodMethod() throws InvalidInputException {
if (input == null) {
throw new InvalidInputException(
"Input cannot be null for operation X");
}
}
Chapter Summary
Exception Concepts:
- Checked vs unchecked exceptions
- Exception hierarchy and inheritance
- try-catch-finally structure
- Exception propagation mechanism
- Resource management with try-with-resources
Practical Skills:
- Handling common runtime exceptions
- Creating custom exception classes
- Using throw and throws keywords
- Implementing proper cleanup code
- Following exception handling best practices
Next: Multithreading
Thank You!
Questions?
Ready to explore Multithreading!

