Custom exceptions in Java — when and how to create them
custom exception class, extending Exception vs RuntimeException, constructors, exception message, chained exceptions, domain-specific errors, exception hierarchy design
Custom Exceptions
Standard exceptions cover generic errors. Domain-specific exceptions communicate business rule violations clearly and let callers handle them precisely without parsing error messages.
Creating a Custom Exception
public class InsufficientFundsException extends RuntimeException {
private final double amount;
private final double balance;
public InsufficientFundsException(double amount, double balance) {
super(String.format(
"Cannot withdraw %.2f — balance is only %.2f", amount, balance));
this.amount = amount;
this.balance = balance;
}
public double getAmount() { return amount; }
public double getBalance() { return balance; }
}
Throwing and Catching
public void withdraw(double amount) {
if (amount > balance) {
throw new InsufficientFundsException(amount, balance);
}
balance -= amount;
}
try {
account.withdraw(5000);
} catch (InsufficientFundsException e) {
System.err.println(e.getMessage());
System.err.println("Shortfall: " + (e.getAmount() - e.getBalance()));
}
Checked vs unchecked custom exceptions: Extend Exception for recoverable domain conditions the caller must address — invalid payment, expired token. Extend RuntimeException for violated invariants where recovery is unlikely.
Include extra fields only when callers need structured data beyond the message string. Always provide at least one constructor that accepts a String message and one that accepts String message, Throwable cause so exception chaining works correctly.
Name custom exceptions clearly after the business condition they represent, not the mechanism. PaymentDeclinedException is better than PaymentProcessingRuntimeException. Callers should understand the meaning from the class name alone, without reading the message.
