Skip to main content
  1. Resources/
  2. Study Materials/
  3. Information & Communication Technology Engineering/
  4. ICT Semester 4/
  5. Java Programming (4343203)/

10 mins· ·
Milav Dabgar
Author
Milav Dabgar
Experienced lecturer in the electrical and electronic manufacturing industry. Skilled in Embedded Systems, Image Processing, Data Science, MATLAB, Python, STM32. Strong education professional with a Master’s degree in Communication Systems Engineering from L.D. College of Engineering - Ahmedabad.
Java Programming - Exception Handling

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

Java 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!