9.4. Throwing Exceptions
Throwing exceptions allows you to explicitly generate exceptions in your code. This is useful for validating inputs, enforcing business rules, or propagating errors.
The throw Keyword
Use the throw keyword to explicitly throw an exception:
java
throw new ExceptionType("Error message");Throwing Checked Exceptions
Checked exceptions must be declared in the method signature or handled.
java
public void processFile(String filename) throws FileNotFoundException {
if (filename == null || filename.trim().isEmpty()) {
throw new FileNotFoundException("Filename cannot be empty");
}
File file = new File(filename);
if (!file.exists()) {
throw new FileNotFoundException("File not found: " + filename);
}
// Process the file
}Throwing Unchecked Exceptions
Unchecked exceptions (RuntimeException and its subclasses) don't need to be declared.
java
public void validateAge(int age) {
if (age < 0) {
throw new IllegalArgumentException("Age cannot be negative: " + age);
}
if (age > 150) {
throw new IllegalArgumentException("Age seems unrealistic: " + age);
}
System.out.println("Valid age: " + age);
}Re-throwing Exceptions
You can catch an exception and throw a different one (exception translation):
java
public void loadConfiguration(String configFile) {
try {
Properties props = new Properties();
props.load(new FileInputStream(configFile));
} catch (IOException e) {
// Translate to a more specific exception
throw new ConfigurationException("Failed to load configuration: " + configFile, e);
}
}Custom Exception with Cause
java
public class ConfigurationException extends RuntimeException {
public ConfigurationException(String message) {
super(message);
}
public ConfigurationException(String message, Throwable cause) {
super(message, cause);
}
}Complete Example: Bank Account
java
public class BankAccount {
private double balance;
private String accountNumber;
public BankAccount(String accountNumber, double initialBalance) {
if (accountNumber == null || accountNumber.trim().isEmpty()) {
throw new IllegalArgumentException("Account number cannot be empty");
}
if (initialBalance < 0) {
throw new IllegalArgumentException("Initial balance cannot be negative");
}
this.accountNumber = accountNumber;
this.balance = initialBalance;
}
public void withdraw(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Withdrawal amount must be positive");
}
if (amount > balance) {
throw new InsufficientFundsException(
"Insufficient funds. Current balance: " + balance + ", requested: " + amount);
}
balance -= amount;
System.out.println("Withdrawn: " + amount + ", New balance: " + balance);
}
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("Deposit amount must be positive");
}
balance += amount;
System.out.println("Deposited: " + amount + ", New balance: " + balance);
}
public double getBalance() {
return balance;
}
}
class InsufficientFundsException extends RuntimeException {
public InsufficientFundsException(String message) {
super(message);
}
}Example: User Registration Validation
java
public class UserValidator {
public void registerUser(String username, String email, int age) {
// Validate username
if (username == null || username.length() < 3) {
throw new IllegalArgumentException("Username must be at least 3 characters long");
}
// Validate email
if (email == null || !email.contains("@")) {
throw new IllegalArgumentException("Invalid email format");
}
// Validate age
if (age < 13) {
throw new IllegalArgumentException("User must be at least 13 years old");
}
if (age > 120) {
throw new IllegalArgumentException("Invalid age specified");
}
// If all validations pass, register user
System.out.println("User registered successfully: " + username);
}
}Best Practices for Throwing Exceptions
Throw early: Validate inputs at method boundaries
Use appropriate exception types:
IllegalArgumentExceptionfor invalid parametersIllegalStateExceptionfor invalid object stateNullPointerExceptionfor null values (sparingly)- Custom exceptions for domain-specific errors
Provide meaningful messages: Include relevant context
Preserve stack traces: When re-throwing, include the original exception
Document thrown exceptions: Use Javadoc
@throwstags
java
/**
* Transfers money between accounts
* @param amount the amount to transfer
* @param targetAccount the target account
* @throws IllegalArgumentException if amount is invalid
* @throws InsufficientFundsException if source account has insufficient funds
*/
public void transfer(double amount, BankAccount targetAccount) {
if (amount <= 0) {
throw new IllegalArgumentException("Transfer amount must be positive: " + amount);
}
if (amount > balance) {
throw new InsufficientFundsException("Insufficient funds for transfer");
}
// Perform transfer
}