Skip to content

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

  1. Throw early: Validate inputs at method boundaries

  2. Use appropriate exception types:

    • IllegalArgumentException for invalid parameters
    • IllegalStateException for invalid object state
    • NullPointerException for null values (sparingly)
    • Custom exceptions for domain-specific errors
  3. Provide meaningful messages: Include relevant context

  4. Preserve stack traces: When re-throwing, include the original exception

  5. Document thrown exceptions: Use Javadoc @throws tags

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
}