Exception Hierarchy in Java
Problem
Every Java exception descends from Throwable through Exception and, for unchecked ones, RuntimeException — a catch block written for an ancestor type still matches any of its descendants, thanks to ordinary object-oriented polymorphism.
Throw a specific exception type but catch it through a more general ancestor class, then confirm which exact type was actually thrown.
Java Program
public class ExceptionHierarchyExample {
public static void main(String[] args) {
try {
int n = Integer.parseInt("abc");
} catch (RuntimeException e) {
// getName() reports the actual thrown type, not the catch clause's declared type
System.out.println("Caught as: " + e.getClass().getName());
}
}
}Output
Core Logic
Catching a RuntimeException reference still captures a NumberFormatException object underneath, since a catch clause matches by the exception's actual type against everything in its ancestry, not just an exact name.
- 1
Integer.parseInt("abc")throws aNumberFormatException, since"abc"isn't a valid integer. - 2
NumberFormatExceptionextendsIllegalArgumentException, which extendsRuntimeException— socatch (RuntimeException e)matches it. - 3
e.getClass().getName()reports the object's real, most specific class —NumberFormatException— regardless of which ancestor type the catch clause named. - 4The variable
eis declared asRuntimeExceptioninside the catch block, but the object it refers to is still, underneath, a fullNumberFormatException.
"abc" throws a NumberFormatException, caught by the RuntimeException clause, and getClass().getName() still reports java.lang.NumberFormatException.Key Point: This is the same polymorphism rule that applies to any Java class hierarchy — catching by a supertype trades away the ability to distinguish which subtype occurred, in exchange for one catch block that handles the whole family of related exceptions.