When a method in Java needs to signal an error condition to its caller, the throw keyword provides the mechanism to do so. Understanding how to properly throw exceptions is essential for building robust applications.
Throwing Exceptions from Methods
The process of throwing an exception from a method involves three key steps:
First, identify the appropriate exception type. Java provides numerous built-in exception classes such as IOException, NullPointerException, and IllegalArgumentException. You can also create your own exception types for domain-specific errors.
Second, declare the exception in the method signature using the throws clause. This informs callers that the method may propagate certain exception types and must be handled accordingly.
Third, use the throw keyword within the method body to instantiate and throw the exception when an error condition is detected. This immediately terminates method execution and transfers control to the caller.
Consider this example demonstrating exception throwing:
public class ValidationUtils {
public static double calculateAverage(int[] values) throws IllegalArgumentException {
if (values == null) {
throw new IllegalArgumentException("Input array must not be null");
}
if (values.length == 0) {
throw new IllegalArgumentException("Input array cannot be empty");
}
int sum = 0;
for (int value : values) {
sum += value;
}
return (double) sum / values.length;
}
public static void main(String[] args) {
try {
int[] numbers = {};
double avg = calculateAverage(numbers);
System.out.println("Average: " + avg);
} catch (IllegalArgumentException error) {
System.out.println("Validation failed: " + error.getMessage());
}
}
}
In this implementation, the calculateAverage method validates its input and throws an IllegalArgumentException when validation fails. The main method wraps the call in a try-catch block to handle the potential exception gracefully.
Creating Custom Exception Classes
Custom exceptions allow you to model domain-specific error conditions and provide meaningful context about what went wrong. Creating a custom exception typically involves extending an existing exception class.
The standard approach involves subclassing Exception for checked exceptions or RuntimeException for unchecked exceptions. Custom exceptions should provide constructors that pass appropriate information to the parent class.
Here is a custom exception implementation:
public class InsufficientFundsException extends RuntimeException {
private final double currentBalance;
private final double requestedAmount;
public InsufficientFundsException(double balance, double requested) {
super(String.format("Insufficient funds: available %.2f, requested %.2f", balance, requested));
this.currentBalance = balance;
this.requestedAmount = requested;
}
public double getCurrentBalance() {
return currentBalance;
}
public double getRequestedAmount() {
return requestedAmount;
}
}
This custom exception stores additional context about the error, including the current account balance and the amount that was requested.
Using the custom exception in practice:
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(balance, amount);
}
balance -= amount;
}
public static void main(String[] args) {
BankAccount account = new BankAccount(100.0);
try {
account.withdraw(150.0);
} catch (InsufficientFundsException e) {
System.out.println("Transaction declined: " + e.getMessage());
System.out.println("Your balance: " + e.getCurrentBalance());
}
}
}
The withdraw method throws the custom exception when withdrawal would exceed the available balance. Callers can catch this specific exception type and access the additional properties to provide detailed feedback.