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
0 Comments