CMP228 Advanced Programming with Java

Advanced Programming with JavaUnit 39 min read

Exception Handling: Try-Catch, Custom Exceptions, Error Propagation

Unit 3 of Advanced Programming with Java covers exception handling mechanisms in Java, including checked vs. unchecked exceptions, try-catch-finally blocks, custom exceptions, and error propagation strategies, with real-world applications in banking, e-commerce, and system robustness.

TAKEAWAYS:

  • Exceptions disrupt normal program flow; Java categorizes them into checked (compile-time) and unchecked (runtime) exceptions.
  • Try-catch-finally blocks isolate error-prone code, execute recovery logic, and ensure cleanup via finally.
  • Custom exceptions extend Exception or RuntimeException for domain-specific error handling (e.g., InsufficientBalanceException in banking).
  • Error propagation uses method signatures to enforce exception handling (checked) or delegate responsibility (unchecked).
  • Logging (java.util.logging or SLF4J) records exceptions for debugging and auditing.
  • Best practices include avoiding empty catch blocks, using specific exception types, and validating inputs early.

1. What Are Exceptions?

Exceptions are runtime anomalies that disrupt normal program execution. Java classifies them into:

  • Checked Exceptions (compile-time enforced, e.g., IOException, SQLException).
  • Unchecked Exceptions (runtime errors, e.g., NullPointerException, ArrayIndexOutOfBoundsException).
  • Errors (serious JVM issues like OutOfMemoryError; typically unrecoverable).
classDiagram
    class Throwable {
        <<abstract>>
        +String getMessage()
    }
    class Error {
        <<unrecoverable>>
        +OutOfMemoryError
        +StackOverflowError
    }
    class Exception {
        <<recoverable>>
        +IOException
        +SQLException
    }
    class RuntimeException {
        <<unchecked>>
        +NullPointerException
        +ArrayIndexOutOfBoundsException
    }
    Throwable <|-- Error
    Throwable <|-- Exception
    Exception <|-- RuntimeException

Why it matters: Unchecked exceptions (e.g., NullPointerException) often indicate programming bugs, while checked exceptions (e.g., FileNotFoundException) signal external issues (e.g., missing files, network failures).


2. Try-Catch-Finally Blocks

Use try-catch to handle exceptions gracefully and finally for cleanup (e.g., closing files).

FileReader objectIOException (unhandled)finally block (executes)TOP
Stack state during file reading: Exception thrown → caught → finally executes

Syntax:

try {
    // Risky code (may throw exceptions)
} catch (ExceptionType1 e1) {
    // Handle ExceptionType1
} catch (ExceptionType2 e2) {
    // Handle ExceptionType2
} finally {
    // Always executes (e.g., release resources)
}

Example: Reading a File

import java.io.*;

public class FileReaderExample {
    public static void main(String[] args) {
        FileReader file = null;
        try {
            file = new FileReader("data.txt");
            int data;
            while ((data = file.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (FileNotFoundException e) {
            System.err.println("File not found: " + e.getMessage());
        } catch (IOException e) {
            System.err.println("Error reading file: " + e.getMessage());
        } finally {
            try {
                if (file != null) file.close(); // Ensure file is closed
            } catch (IOException e) {
                System.err.println("Error closing file: " + e.getMessage());
            }
        }
    }
}

Trace Table:

Step Action Exception Thrown Output
1 file = new FileReader("data.txt") FileNotFoundException "File not found: data.txt"
2 file.read() None Prints file content
3 file.close() IOException "Error closing file: ..."

3. Custom Exceptions

Extend Exception or RuntimeException to create domain-specific exceptions.

ExceptionRuntimeExceptionInsufficientBalanceExceptionIOException
Custom exception hierarchy for banking system

Example: Banking Withdrawal

class InsufficientBalanceException extends Exception {
    public InsufficientBalanceException(String message) {
        super(message);
    }
}

class BankAccount {
    private double balance;

    public void withdraw(double amount) throws InsufficientBalanceException {
        if (amount > balance) {
            throw new InsufficientBalanceException("Insufficient balance. Current: " + balance);
        }
        balance -= amount;
    }
}

Real-World Tie-In: Khalti uses custom exceptions to validate transactions. For example:

  • InvalidAmountException: If a user enters a negative amount.
  • PaymentFailedException: If the bank declines the transaction.

4. Exception Propagation

Exceptions can propagate up the call stack until caught. Use throws to declare exceptions in method signatures.

sequenceDiagram
    participant Main
    participant DataProcessor
    participant FileReader
    Main->>DataProcessor: processFile()
    DataProcessor->>FileReader: new FileReader("data.txt")
    FileReader-->>DataProcessor: IOException
    DataProcessor-->>Main: IOException
    Main->>Main: catch (IOException e)
Exception propagation from FileReader → DataProcessor → Main

Example: Propagating IOException

public class DataProcessor {
    public void processFile() throws IOException {
        FileReader file = new FileReader("data.txt"); // May throw IOException
        // ...
    }
}

public class Main {
    public static void main(String[] args) {
        DataProcessor processor = new DataProcessor();
        try {
            processor.processFile(); // Exception propagated here
        } catch (IOException e) {
            System.err.println("Processing failed: " + e.getMessage());
        }
    }
}

Comparison Table:

Approach Use Case Example
Catch in same method Handle locally (e.g., log error) catch (SQLException e)
Declare with throws Delegate to caller public void readFile() throws IOException
Wrap exception Convert to a higher-level type throw new DataAccessException("Failed to read", e)

5. Best Practices

  1. Avoid empty catch blocks: Log exceptions or rethrow them.
    // Bad: Silently ignores errors
    catch (Exception e) {}
    
    // Good: Logs the error
    catch (Exception e) {
        logger.severe("Error: " + e.getMessage());
    }
    
  2. Catch specific exceptions: Avoid broad catch (Exception e).
  3. Use finally for cleanup: Close files, database connections, etc.
  4. Validate inputs early: Prevent exceptions like NullPointerException.
  5. Use try-with-resources (Java 7+) for auto-closing resources:
    try (FileReader file = new FileReader("data.txt")) {
        // Auto-closes file
    } catch (IOException e) {
        // Handle exception
    }
    

In the Real World

  1. eSewa (Nepal):

    • Uses custom exceptions like InvalidOTPException or InsufficientFundsException to handle payment failures gracefully.
    • Example: If a user enters the wrong OTP 3 times, eSewa throws a MaxRetriesExceededException and locks the account temporarily.
  2. Pathao (Ride-Hailing):

    • Propagates exceptions like DriverUnavailableException or RouteNotFoundException to the app’s UI layer, where users see friendly messages like “No drivers available in your area.”
  3. Nepal Stock Exchange (NEPSE):

    • Validates trades using checked exceptions (e.g., InvalidShareQuantityException) to ensure compliance with market rules before processing orders.

Exam Tip

  1. Understand the difference between checked and unchecked exceptions—this is a high-weight question in TU/PU exams.
  2. Practice writing try-catch-finally blocks for file I/O, database operations, and network calls.
  3. Know when to use custom exceptions: Always tie them to domain logic (e.g., InvalidAgeException for age validation).
  4. Trace exception propagation: Exams may ask you to predict which method catches an exception in a multi-level call stack.
  5. Memorize key methods:
    • e.getMessage(): Get exception details.
    • e.printStackTrace(): Print stack trace (debugging).
    • throw new Exception("message"): Manually throw an exception.

Visual Summary:

flowchart TD
    A["Start"] --> B["Try Block"]
    B --> C{"Exception?"}
    C -->|"Yes"| D["Catch Block"]
    C -->|"No"| E["Finally Block"]
    D --> E
    E --> F["End"]
    D -->|"Rethrow"| F

In the real world

  • Khalti (Digital Payment) uses custom exceptions like InvalidAmountException and PaymentFailedException to handle transaction errors. When a user enters an invalid amount (e.g., negative), Khalti throws InvalidAmountException and prompts for correction.
  • Pathao (Ride-Hailing) propagates DriverUnavailableException to the app’s UI when no drivers are available in a zone. The app displays a user-friendly message instead of crashing.
  • Nepal Rastra Bank (NRB) enforces checked exceptions (e.g., InsufficientFundsException) in banking APIs to ensure financial transactions validate balances before processing.

Based on the PU BE Computer (PU) syllabus for Advanced Programming with Java (CMP228), unit 3.

Discussion

Loading…