Introduction
Rethrowing means catching an exception and throwing it again to be handled at a higher level.
Why Rethrow?
- Log and propagate - log locally, handle elsewhere
- Partial handling - do some cleanup, let caller finish
- Add context - wrap with more information
- Chain responsibility - multiple handlers
Basic Syntax
try {
// code
} catch (Exception e) {
// Log or partial handling
throw e; // Rethrow same exception
}
Simple Example
class Demo {
void method1() throws Exception {
throw new Exception("Error in method1");
}
void method2() throws Exception {
try {
method1();
} catch (Exception e) {
System.out.println("Caught in method2: " + e.getMessage());
throw e; // Rethrow
}
}
}
public class Main {
public static void main(String[] args) {
Demo d = new Demo();
try {
d.method2();
} catch (Exception e) {
System.out.println("Caught in main: " + e.getMessage());
}
}
}
Output:
Caught in method2: Error in method1
Caught in main: Error in method1
Log and Rethrow
import java.io.*;
class FileProcessor {
void processFile(String filename) throws IOException {
try {
FileReader fr = new FileReader(filename);
// Process file
} catch (IOException e) {
// Log the error
System.err.println("Error processing file: " + filename);
System.err.println("Error: " + e.getMessage());
// Rethrow for caller to handle
throw e;
}
}
}
public class Main {
public static void main(String[] args) {
FileProcessor processor = new FileProcessor();
try {
processor.processFile("missing.txt");
} catch (IOException e) {
System.out.println("Main: File operation failed");
}
}
}
Wrapping Exception
Wrap original exception in new one with more context.
import java.io.*;
class ApplicationException extends Exception {
ApplicationException(String message, Throwable cause) {
super(message, cause);
}
}
class DataLoader {
void loadData(String filename) throws ApplicationException {
try {
FileReader fr = new FileReader(filename);
// Load data
} catch (IOException e) {
// Wrap in application-specific exception
throw new ApplicationException("Failed to load data from: " + filename, e);
}
}
}
public class Main {
public static void main(String[] args) {
DataLoader loader = new DataLoader();
try {
loader.loadData("data.txt");
} catch (ApplicationException e) {
System.out.println("Application error: " + e.getMessage());
System.out.println("Caused by: " + e.getCause());
}
}
}
Exception Chaining
Link exceptions to show cause.
class Level3 {
void method() throws Exception {
throw new Exception("Level 3 error");
}
}
class Level2 {
void method() throws Exception {
try {
new Level3().method();
} catch (Exception e) {
throw new Exception("Level 2 error", e); // Chain
}
}
}
class Level1 {
void method() throws Exception {
try {
new Level2().method();
} catch (Exception e) {
throw new Exception("Level 1 error", e); // Chain
}
}
}
public class Main {
public static void main(String[] args) {
try {
new Level1().method();
} catch (Exception e) {
System.out.println("Main caught: " + e.getMessage());
// Trace the chain
Throwable cause = e.getCause();
while (cause != null) {
System.out.println("Caused by: " + cause.getMessage());
cause = cause.getCause();
}
}
}
}
Output:
Main caught: Level 1 error
Caused by: Level 2 error
Caused by: Level 3 error
Cleanup and Rethrow
import java.io.*;
class DatabaseConnection {
void connect() throws Exception {
System.out.println("Connecting...");
throw new Exception("Connection failed");
}
void close() {
System.out.println("Connection closed");
}
}
class DataService {
void fetchData() throws Exception {
DatabaseConnection conn = new DatabaseConnection();
try {
conn.connect();
// Fetch data
} catch (Exception e) {
// Cleanup before rethrowing
conn.close();
System.out.println("Cleaned up resources");
throw e; // Rethrow
}
}
}
public class Main {
public static void main(String[] args) {
DataService service = new DataService();
try {
service.fetchData();
} catch (Exception e) {
System.out.println("Error: " + e.getMessage());
}
}
}
Complete Example: Transaction
class InsufficientFundsException extends Exception {
InsufficientFundsException(String message) {
super(message);
}
}
class TransactionException extends Exception {
TransactionException(String message, Throwable cause) {
super(message, cause);
}
}
class BankAccount {
private double balance;
BankAccount(double balance) {
this.balance = balance;
}
void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
throw new InsufficientFundsException("Balance: $" + balance + ", Requested: $" + amount);
}
balance -= amount;
System.out.println("Withdrawn: $" + amount);
}
double getBalance() {
return balance;
}
}
class TransactionService {
void processWithdrawal(BankAccount account, double amount) throws TransactionException {
try {
System.out.println("Processing withdrawal...");
account.withdraw(amount);
System.out.println("Transaction successful");
} catch (InsufficientFundsException e) {
// Log transaction failure
System.out.println("Transaction failed: " + e.getMessage());
// Wrap and rethrow with context
throw new TransactionException("Withdrawal failed for amount: $" + amount, e);
}
}
}
public class Main {
public static void main(String[] args) {
BankAccount account = new BankAccount(1000);
TransactionService service = new TransactionService();
try {
service.processWithdrawal(account, 500); // Success
service.processWithdrawal(account, 1000); // Fail
} catch (TransactionException e) {
System.out.println("\nMain handler:");
System.out.println("Error: " + e.getMessage());
System.out.println("Root cause: " + e.getCause().getMessage());
}
}
}
Conditional Rethrowing
import java.io.*;
class FileHandler {
void readFile(String filename) throws IOException {
try {
FileReader fr = new FileReader(filename);
// Read file
} catch (FileNotFoundException e) {
// Handle file not found differently
System.out.println("Creating new file: " + filename);
// Don't rethrow - handled
} catch (IOException e) {
// Other IO errors - rethrow
System.out.println("Unexpected IO error");
throw e;
}
}
}
public class Main {
public static void main(String[] args) {
FileHandler handler = new FileHandler();
try {
handler.readFile("test.txt");
} catch (IOException e) {
System.out.println("Main: " + e.getMessage());
}
}
}
Rethrowing vs New Exception
Rethrow Same:
try {
// code
} catch (Exception e) {
System.out.println("Log: " + e.getMessage());
throw e; // Same exception, same stack trace
}
Throw New:
try {
// code
} catch (Exception e) {
throw new Exception("New error"); // New exception, new stack trace
}
Wrap (Best):
try {
// code
} catch (Exception e) {
throw new MyException("Context", e); // Preserves original
}
getCause() Method
public class Main {
public static void main(String[] args) {
try {
method1();
} catch (Exception e) {
System.out.println("Exception: " + e.getMessage());
Throwable cause = e.getCause();
if (cause != null) {
System.out.println("Cause: " + cause.getMessage());
}
}
}
static void method1() throws Exception {
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
throw new Exception("Calculation failed", e);
}
}
}
When to Rethrow
Rethrow when:
- Need to log but can’t fully handle
- Cleanup required before propagating
- Adding context to exception
- Multiple levels need to handle
Don’t rethrow when:
- Can fully handle the error
- No value in propagating
- Already at top level
Best Practices
- Log before rethrowing
- Wrap with context
- Use exception chaining
- Clean up resources
- Don’t lose original exception
- Document rethrown exceptions
Quick Reference
// Simple rethrow
try {
// code
} catch (Exception e) {
System.out.println("Logging: " + e.getMessage());
throw e; // Rethrow
}
// Wrap and rethrow
try {
// code
} catch (IOException e) {
throw new MyException("Context", e);
}
// Conditional rethrow
try {
// code
} catch (Exception e) {
if (canHandle(e)) {
handle(e);
} else {
throw e; // Can't handle, rethrow
}
}
// Get cause
try {
// code
} catch (Exception e) {
Throwable cause = e.getCause();
while (cause != null) {
System.out.println(cause.getMessage());
cause = cause.getCause();
}
}
Common Mistakes
// ✗ Wrong - losing original exception
try {
// code
} catch (IOException e) {
throw new Exception("Error"); // Lost original!
}
// ✓ Correct - preserve original
try {
// code
} catch (IOException e) {
throw new Exception("Error", e); // Chained
}
// ✗ Wrong - empty log and rethrow
try {
// code
} catch (Exception e) {
// Nothing here
throw e; // Why catch if doing nothing?
}
// ✓ Correct - add value
try {
// code
} catch (Exception e) {
System.err.println("Context: " + someInfo);
cleanupResources();
throw e;
}
Exam Tips
Remember:
- Rethrow = catch and throw again
- throw e to rethrow same
- Wrap for context
- getCause() retrieves original
- Chain exceptions for tracing
- Log before rethrowing
- Cleanup before rethrowing
- Preserve original exception
- Must declare rethrown checked exceptions
- Document exception propagation
Common Questions:
- What is rethrowing?
- How to rethrow exception?
- Rethrowing vs new exception?
- Exception chaining?
- getCause() method?
- When to rethrow?
- Wrapping exceptions?
- Best practices?
- Preserving stack trace?
- Why rethrow?