Custom Exception Classes
Custom Exception Classes
The exceptions Java ships with — IllegalArgumentException, IOException, NullPointerException — are generic. When you read a stack trace that says IllegalArgumentException: invalid value you still have to guess which value was invalid and why. Custom exception classes solve this: they give a precise name to the problem, they live in your domain, and they can carry extra context that generic exceptions cannot.
Why Create Your Own Exceptions?
- Meaningful names.
InsufficientFundsExceptioncommunicates the problem instantly;RuntimeException: balance too lowdoes not. - Catch selectively. Callers can catch only the exact failure they know how to handle, without accidentally silencing unrelated problems.
- Carry domain data. A custom class can store the account number, the requested amount, and the current balance — not just a string message.
- Cleaner APIs. A method signature like
throws InsufficientFundsExceptiondocuments intent far better thanthrows Exception.
Extending Exception vs. RuntimeException
This is the most important design choice. The rule from Lesson 4 still applies:
- Extend
Exception(checked) when the caller should handle the failure — file not found, network timeout, business-rule violations the caller can recover from. - Extend
RuntimeException(unchecked) when the failure is a programming mistake — invalid argument, wrong state, broken contract — that callers should not normally catch.
InsufficientFundsException extends Exception is the right choice. If an internal utility receives a null it was never supposed to receive, extends RuntimeException is cleaner.
The Minimal Pattern
Every custom exception needs at minimum a constructor that accepts a message and forwards it to the parent:
That is all you need to throw it and catch it by name:
Adding a Cause Constructor
Always provide a constructor that accepts a Throwable cause. This lets you wrap lower-level exceptions without losing the original stack trace — a practice covered in Lesson 6 (rethrowing).
Storing Domain-Specific Data
A custom class can hold any fields that help callers react intelligently to the failure:
Now a caller can present a helpful message to the user without parsing a string:
Building a Small Exception Hierarchy
For a larger application, group related exceptions under a common base so callers can catch broadly or narrowly:
A payment gateway handler can catch all payment problems with catch (PaymentException e), while a refund module that only cares about declined cards can catch CardDeclinedException specifically.
Naming Convention
Every custom exception class name should end with Exception — ValidationException, OrderNotFoundException, RateLimitExceededException. Java developers expect this; breaking the convention confuses readers.
Error or Throwable directly. Error is reserved for JVM-level failures (out of memory, stack overflow). Extending Throwable bypasses the checked/unchecked distinction entirely and breaks assumptions throughout the language. Always extend Exception or RuntimeException.
Summary
Custom exception classes make your code self-documenting and easier to maintain. Extend Exception for recoverable domain failures, extend RuntimeException for programming errors. Always provide a message constructor and a cause constructor. Add fields when callers need structured data beyond a message string. Keep hierarchies flat — a well-named base and a handful of concrete subtypes is the right shape for most applications.