Multi-Catch and Custom Exceptions in Java

Master multi-catch blocks, custom exceptions, creating and throwing custom exceptions, and exception best practices for the OCP 21 exam.

Table of Contents

1. Multi-Catch Blocks

Multi-catch blocks (Java 7+) allow catching multiple exception types in a single catch block using the pipe operator (| ).

1.1 Multi-Catch Syntax

Syntax:
try {
    // Code that might throw exceptions }
catch (ExceptionType1 | ExceptionType2 | ExceptionType3 e) {
    // Handle all exception types
}

1.2 Examples

Example:
import java.io.*;
// Multi-catch for related exceptions try {
    FileReader file = new FileReader("file.txt");
    file.read();
}
catch (FileNotFoundException | IOException e) {
    System.out.println("File error:" + e.getMessage());
} // Multi-catch for different exception types try {
int[] arr = new int[5];
arr[10] = 100;
// ArrayIndexOutOfBoundsException String str = null;
int len = str.length();
// NullPointerException }
catch (ArrayIndexOutOfBoundsException | NullPointerException e) {
    System.out.println("Error:" + e.getClass().getSimpleName());
} // Before Java 7 (separate catch blocks) try {
// code }
catch (FileNotFoundException e) {
    // Handle }
catch (IOException e) {
    // Handle (same code as above - duplication!) } // Java 7+ (multi-catch - no duplication) try {
    // code }
catch (FileNotFoundException | IOException e) {
    // Handle both with same code
}

2. Multi-Catch Rules

2.1 Important Rules

  • Exception types in multi-catch must be unrelated (not in inheritance hierarchy)
  • The catch parameter is implicitly final
  • Cannot reassign the exception variable
  • Can use multi-catch with try-with-resources

2.2 Valid Multi-Catch

Example:
// Valid - unrelated exceptions try {
    // code }
catch (FileNotFoundException | SQLException e) {
    // OK - unrelated exception types } // Valid - RuntimeException subclasses try {
    // code }
catch (NullPointerException | IllegalArgumentException e) {
    // OK - both are RuntimeException subclasses, but unrelated } // Valid - with try-with-resources try (FileReader file = new FileReader("file.txt")) {
    // code }
catch (FileNotFoundException | IOException e) {
    // Handle
}

2.3 Invalid Multi-Catch

Invalid Examples:
// INVALID - FileNotFoundException extends IOException try {
    // code }
catch (FileNotFoundException | IOException e) {
    // Compilation error - related exceptions! } // INVALID - Cannot reassign (implicitly final) try {
    // code }
catch (IOException | SQLException e) {
    e = new IOException();
    // Compilation error - e is final } // INVALID - Exception and its subclass try {
    // code }
catch (Exception | RuntimeException e) {
    // Compilation error - RuntimeException extends Exception
}

2.4 Using Exception Variable

Example:
// Can only access common methods try {
    // code }
catch (FileNotFoundException | SQLException e) {
    // Can use methods from common supertype System.out.println(e.getMessage());
    e.printStackTrace();
    // Cannot use specific methods without instanceof check if (e instanceof FileNotFoundException) {
        FileNotFoundException fnfe = (FileNotFoundException) e;
        // Now can use FileNotFoundException-specific methods }
}

3. Custom Exceptions

Custom exceptions allow you to create application-specific exception types that better represent error conditions in your domain.

3.1 Why Create Custom Exceptions?

  • Better error representation for your domain
  • More specific error handling
  • Better code readability
  • Ability to add custom fields and methods
  • Better error messages

3.2 When to Create Custom Exceptions

  • When standard exceptions don't adequately represent the error
  • When you need to add additional context or information
  • When you want to force callers to handle specific error conditions
  • When you need exception-specific behavior

4. Creating Custom Exceptions

4.1 Checked Custom Exception

Example:
// Custom checked exception class InsufficientFundsException extends Exception {
    private double balance;
    private double amount;
    public InsufficientFundsException(double balance, double amount) {
        super("Insufficient funds. Balance:" + balance +", Required:" + amount);
        this.balance = balance;
        this.amount = amount;
    }
public double getBalance() {
    return balance;
}
public double getAmount() {
    return amount;
}
} // Using custom checked exception class Account {
private double balance;
public void withdraw(double amount) throws InsufficientFundsException {
    if (amount > balance) {
        throw new InsufficientFundsException(balance, amount);
    }
balance -= amount;
}
}

4.2 Unchecked Custom Exception

Example:
// Custom unchecked exception class InvalidAgeException extends RuntimeException {
    private int age;
    public InvalidAgeException(int age) {
        super("Invalid age:" + age +". Age must be between 0 and 150.");
        this.age = age;
    }
public InvalidAgeException(int age, String message) {
    super(message);
    this.age = age;
}
public int getAge() {
    return age;
}
} // Using custom unchecked exception class Person {
private int age;
public void setAge(int age) {
    if (age < 0 || age > 150) {
        throw new InvalidAgeException(age);
    }
this.age = age;
}
}

4.3 Custom Exception Best Practices

  • Extend appropriate exception type (Exception for checked, RuntimeException for unchecked)
  • Provide constructors matching parent class
  • Add meaningful error messages
  • Include relevant context (fields)
  • Follow naming convention: end with"Exception"
  • Provide getters for custom fields
Example:
// Well-designed custom exception class ValidationException extends Exception {
    private String field;
    private String value;
    // Default constructor public ValidationException(String message) {
        super(message);
    } // Constructor with field information public ValidationException(String field, String value, String message) {
    super("Validation failed for field'" + field +"' with value'" + value +"':" + message);
    this.field = field;
    this.value = value;
} // Constructor with cause public ValidationException(String message, Throwable cause) {
super(message, cause);
}
public String getField() {
    return field;
}
public String getValue() {
    return value;
}
}

5. Throwing Exceptions

The throw keyword is used to throw an exception explicitly.

5.1 throw Keyword

Example:
// Throwing standard exception public void divide(int a, int b) {
    if (b == 0) {
        throw new ArithmeticException("Division by zero");
    }
int result = a / b;
} // Throwing custom exception public void validateAge(int age) throws InvalidAgeException {
if (age < 0 || age > 150) {
    throw new InvalidAgeException(age);
}
} // Throwing checked exception public void processFile(String filename) throws IOException {
if (filename == null || filename.isEmpty()) {
    throw new IOException("Filename cannot be null or empty");
} // Process file } // Throwing in constructor public Person(String name, int age) {
if (name == null) {
    throw new IllegalArgumentException("Name cannot be null");
}
this.name = name;
this.age = age;
}

5.2 throws Clause

The throws clause declares that a method may throw certain exceptions.

Example:
// Declaring checked exceptions public void readFile(String filename) throws IOException {
    FileReader file = new FileReader(filename);
    // ... } // Declaring multiple exceptions public void processData(String data) throws IOException, SQLException, ValidationException {
    // Method may throw any of these exceptions } // Unchecked exceptions don't need to be declared public void divide(int a, int b) {

            // No need to declare ArithmeticException if (b == 0) {

                throw new ArithmeticException();
            }
        } // But can be declared (optional) public void divide(int a, int b) throws ArithmeticException {

            if (b == 0) {
                throw new ArithmeticException();
            }
        }

6. Exception Chaining

Exception chaining allows wrapping one exception inside another, preserving the original exception information.

6.1 Wrapping Exceptions

Example:
import java.io.*;
// Wrapping checked exception in unchecked public void readConfigFile(String filename) {
    try {
        FileReader file = new FileReader(filename);
    }
catch (IOException e) {
    throw new RuntimeException("Failed to read config file", e);
    // Original IOException is preserved as cause }
} // Wrapping in custom exception public void processData(String data) throws DataProcessingException {
try {
    // Process data }
catch (IOException e) {
    throw new DataProcessingException("Error processing data", e);
}
} // Accessing cause try {
readConfigFile("config.txt");
}
catch (RuntimeException e) {
    Throwable cause = e.getCause();
    // Original IOException if (cause instanceof IOException) {
        IOException ioException = (IOException) cause;
        // Handle original exception }
}

6.2 Exception Constructors

Example:
// Exception constructors for chaining class CustomException extends Exception {
    // Constructor with message public CustomException(String message) {
        super(message);
    } // Constructor with cause public CustomException(Throwable cause) {
    super(cause);
} // Constructor with message and cause public CustomException(String message, Throwable cause) {
super(message, cause);
}
} // Using constructors try {
// code that throws IOException }
catch (IOException e) {
    // Wrap with custom exception throw new CustomException("Custom error occurred", e);
}

7. Exception Best Practices

7.1 Best Practices

  • Use Specific Exceptions: Catch specific exceptions, not general Exception
  • Don't Swallow Exceptions: Always handle or log exceptions
  • Provide Meaningful Messages: Include context in exception messages
  • Preserve Stack Traces: Use exception chaining when wrapping
  • Fail Fast: Throw exceptions early when invalid state detected
  • Document Exceptions: Use JavaDoc to document thrown exceptions
  • Use Checked for Recoverable: Use checked exceptions for recoverable conditions
  • Use Unchecked for Programming Errors: Use unchecked for programming errors

7.2 Anti-Patterns to Avoid

Avoid These:
// DON'T: Empty catch block try {

    // code }

    catch (Exception e) {
        // Swallowing exception - bad! } // DON'T: Catching Exception everywhere try {
    // code }
catch (Exception e) {
    // Too general - loses specific error information } // DON'T: Throwing Exception public void method() throws Exception {

                    // Too general // code } // DON'T: Catching and ignoring try {
    // code }
catch (IOException e) {
    // Ignoring - should at least log } // DO: Specific exceptions and proper handling try {
    // code }
catch (FileNotFoundException e) {
    logger.error("File not found", e);
    // Handle appropriately }
catch (IOException e) {
    logger.error("IO error", e);
    // Handle appropriately
}

8. Exam Key Points

Critical Concepts for OCP 21 Exam:

  • Multi-Catch: Catch multiple exceptions in one block using pipe (|)
  • Multi-Catch Syntax: catch (Ex1 | Ex2 | Ex3 e)
  • Unrelated Exceptions: Exceptions in multi-catch must not be related (not in hierarchy)
  • Implicitly Final: Exception variable in multi-catch is final
  • Cannot Reassign: Cannot reassign exception variable in multi-catch
  • Common Methods: Can only access methods common to all exception types
  • Custom Exception: Extend Exception (checked) or RuntimeException (unchecked)
  • Naming Convention: End custom exception names with"Exception"
  • Constructors: Provide constructors matching parent class
  • throw Keyword: Used to throw exceptions explicitly
  • throws Clause: Declares exceptions method may throw
  • Checked Exception: Must be declared in throws or caught
  • Unchecked Exception: No need to declare (but can)
  • Exception Chaining: Wrap exceptions to preserve original cause
  • getCause(): Returns the cause exception
  • Exception Constructors: (String), (Throwable), (String, Throwable)
  • Fail Fast: Throw exceptions early when invalid state detected
  • Meaningful Messages: Include context in exception messages
  • Don't Swallow: Always handle or log exceptions
  • Specific Exceptions: Catch specific exceptions, not general Exception

Post a Comment

0 Comments