Lesson 32 / 38
Custom Exceptions & Multi-Catch
Design your own exception types that carry meaningful context, and handle several exception types in one concise catch clause.
A custom checked exception
Extend Exception for a checked exception callers must handle, or RuntimeException for an unchecked one signalling a programming error. Always chain the original cause so the stack trace stays complete.
public class InsufficientFundsException extends Exception {
private final double shortfall;
public InsufficientFundsException(double shortfall) {
super("short by " + shortfall);
this.shortfall = shortfall;
}
public double getShortfall() { return shortfall; }
}
// caller:
void withdraw(double amt) throws InsufficientFundsException {
if (amt > balance) throw new InsufficientFundsException(amt - balance);
balance -= amt;
}Multi-catch
When two exception types need the same handling, catch both with | instead of duplicating the block. The caught variable is effectively final and typed as the common supertype.
try {
process(file);
} catch (IOException | ParseException e) {
log.error("could not process file", e);
throw new RuntimeException(e);
}Rethrow with context, don't hide it
Catching a low-level exception just to throw a more meaningful one is good practice — as long as you pass the original as the cause (new ServiceException("...", e)), never dropping it, or the real root cause becomes invisible in production logs.
Quick check
Quick check: Which base class should a custom exception extend if callers should NOT be forced to declare or catch it?
- Exception
- Throwable
- RuntimeException
- Error
Answer
RuntimeException — RuntimeException (and its subclasses) are unchecked — the compiler does not require a throws declaration or a catch block.