MA-JAVA0 · Module 20
Custom exception types and hierarchies
Topics: extends Exception · extends RuntimeException · Chaining · When checked
Overview
Session 20 — Custom exception types and hierarchies
Standard types like IOException and IllegalArgumentException cover many cases, yet libraries often introduce domain-specific exceptions so callers can catch precise faults without string matching. This session covers extends Exception versus extends RuntimeException, constructors that attach messages and causes, and when checked exceptions still earn their place.
Time: about 85 minutes.
Concepts
1. Checked vs unchecked custom types
extends Exception— checked: callers mustcatchorthrows—use when recovery is common and you want compile-time enforcement (file format, protocol violations).extends RuntimeException— unchecked: for contract breaks similar toIllegalArgumentException—bad caller input, impossible state after internal bug.
public class BadRowException extends Exception {
public BadRowException(String message) {
super(message);
}
public BadRowException(String message, Throwable cause) {
super(message, cause);
}
}
2. Naming and granularity
Prefer MalformedLedgerException over Exception1. Too many types overwhelm callers; too few force brittle instanceof chains. Aim for one type per recovery strategy.
3. Documenting intent in messages
Include row numbers, field names, and offending snippets (short) so logs are actionable:
throw new BadRowException("line " + lineNo + ": expected 3 columns, got " + parts.length);
4. Wrapping low-level causes
When Integer.parseInt fails inside your parser, wrap:
} catch (NumberFormatException ex) {
throw new BadRowException("quantity not int at line " + lineNo, ex);
}
Callers that catch BadRowException can still call getCause() to inspect the original NumberFormatException.
5. Avoid controlling flow with exceptions
Exceptions are for exceptional paths—do not use them for ordinary loop termination; JVM and readers both suffer.
6. Serialization note (awareness only)
Long ago, many custom exceptions implemented Serializable with a serialVersionUID for RMI-era stacks. Modern services rarely rely on that—know the keyword exists, defer details until a serialization course.
7. Pitfalls
- Swallowing your new checked exception in an empty
catch. - Declaring
throws Exceptionon public APIs—too broad; declare specific types. - Extending
Throwabledirectly—almost always wrong; stick toExceptionsubtree.
Examples
Example A — checked domain exception
public class InsufficientStockException extends Exception {
private final String sku;
private final int requested;
private final int available;
public InsufficientStockException(String sku, int requested, int available) {
super("sku " + sku + ": need " + requested + " have " + available);
this.sku = sku;
this.requested = requested;
this.available = available;
}
public String sku() {
return sku;
}
public int shortfall() {
return requested - available;
}
}
Callers that can reorder stock catch InsufficientStockException specifically; others propagate with throws.
Example B — unchecked invariant breach
public class InvariantBrokenException extends RuntimeException {
public InvariantBrokenException(String message) {
super(message);
}
}
Use when an object’s internal state should be impossible if callers respected earlier contracts—similar spirit to IllegalStateException but domain-named.
Practice check — custom exception types
Multiple-choice check from the Matheion app lesson. Pick an answer for each question, then submit.
1.Extending `Exception` (not `RuntimeException`) creates…
2.Extending `RuntimeException` creates…
3.Exception chaining (`cause`) is useful to…
4.When are checked exceptions a reasonable design?
5.A custom exception class should usually…