Custom exceptions in Java
Extending Exception or RuntimeException, adding context.
Pick a parent
Create your own exception by extending **Exception — making it checked — or RuntimeException — making it unchecked**. It inherits that status, so a RuntimeException subclass never forces callers to catch or declare it. Name it ...Exception.
Pass it up to super
Constructors should pass the message — and ideally the cause — to **super(message) or super(message, cause)**. Throwable stores them for getMessage() and getCause(). Add fields for context, like the missing amount.
class InsufficientFundsException
extends Exception {
final long missing;
InsufficientFundsException(long missing) {
super("short by " + missing);
this.missing = missing;
}
}Your own message
What does this print?
class SoldOut extends RuntimeException {
SoldOut(String s) { super(s + " is gone"); }
}
void main() {
try {
throw new SoldOut("Cake");
} catch (RuntimeException e) {
System.out.println(e.getMessage());
}
}Cake is gonenullSoldOut
Show the answer
The constructor passed the full text to super(...), which stores it in Throwable, so getMessage() returns it. And a SoldOut is a RuntimeException, so that catch matches.
The lost cause
This constructor accepts a cause and then drops it: super(msg) ignores cause, so getCause() returns null and the root problem vanishes from stack traces. Use **super(msg, cause)**.
class DataException extends Exception {
DataException(String msg, Throwable cause) {
super(msg); // cause is lost!
}
}Checked by inheritance
class PaymentDeclined extends Exception { }. A method throws it without catching or declaring it. What happens?
Think about it, then reveal the answer
Compile error. PaymentDeclined extends Exception (not RuntimeException), so it's checked: the method must catch it or declare throws PaymentDeclined.
Precise types pay off
Throwing InsufficientFundsException instead of new RuntimeException("insufficient funds") lets handlers catch exactly that case and read structured data (e.missing) instead of parsing message strings. Web frameworks map such types straight to HTTP responses, like 402 or 409.
Key takeaways
- extends Exception → checked; extends RuntimeException → unchecked
- Call super(message) or super(message, cause)
- Add context fields (ids, amounts) for handlers and logs
- Name it ...Exception
Creating an exception is costly mainly because it records the stack trace. Performance-critical code sometimes overrides fillInStackTrace() to skip that work.
Practice questions
What does this print?
class OutOfStock extends RuntimeException {
OutOfStock(String item) { super(item + " sold out"); }
}
void main() {
try {
throw new OutOfStock("Tea");
} catch (RuntimeException e) {
System.out.println(e.getMessage());
}
}- Tea sold out
- null
- OutOfStock
- Tea
Check your answer
Tea sold out. The constructor passes the message to super, which stores it in Throwable. getMessage() returns it.
What does this print?
class OverdrawnException extends Exception { }
void withdraw() {
throw new OverdrawnException();
}
void main() {
withdraw();
}- Throws OverdrawnException
- Compile error
- Prints nothing
Check your answer
Compile error. OverdrawnException extends Exception, so it's checked. withdraw() must catch it or declare throws OverdrawnException.