Home/Java/Exception Hierarchy in Java

Exception Hierarchy in Java

Learn about the family tree of exceptions and errors

💡 Think of the exception hierarchy like a family tree - everyone is related! At the top is Throwable (the great-grandparent), then it splits into Error (system problems) and Exception (problems you can handle). Just like in a family, kids inherit traits from their parents!

🌳 What is Exception Hierarchy?

Java's exception hierarchy is a tree structure where all exceptions inherit from the Throwable class. This hierarchy determines how exceptions behave and whether they must be caught or declared.

ExceptionHierarchy.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/*
* JAVA EXCEPTION HIERARCHY
*
* Throwable (root)
* / \
* / \
* Error Exception
* | |
* | |
* (System Errors) RuntimeException (Checked Exceptions)
* |
* |
* (Unchecked Exceptions)
*
*
* Throwable
* ├── Error (unchecked - system problems)
* ├── OutOfMemoryError
* ├── StackOverflowError
* ├── VirtualMachineError
* └── ...
*
* └── Exception
* ├── RuntimeException (unchecked - programming errors)
* ├── NullPointerException
* ├── ArrayIndexOutOfBoundsException
* ├── IllegalArgumentException
* ├── ArithmeticException
* └── ...
*
* └── Checked Exceptions (must be handled)
* ├── IOException
* ├── SQLException
* ├── ClassNotFoundException
* ├── InterruptedException
* └── ...
*/
public class ExceptionHierarchy {
public static void main(String[] args) {
System.out.println("All exceptions inherit from Throwable!");
System.out.println("This inheritance determines their behavior.");
}
}

🔍 📊 The Exception Family Tree

Understanding the main branches:

1

Throwable

The root of all exceptions and errors (great-grandparent)

2

Error

Serious system problems (usually can't recover)

3

Exception

Problems your code can catch and handle

4

RuntimeException

Unchecked exceptions (optional to catch)

HierarchyDemo.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class HierarchyDemo {
public static void main(String[] args) {
// All exceptions are Throwable
Throwable t1 = new Exception("I'm an Exception");
Throwable t2 = new RuntimeException("I'm a RuntimeException");
Throwable t3 = new Error("I'm an Error");
System.out.println("All are Throwable:");
System.out.println(" " + t1.getClass().getSimpleName());
System.out.println(" " + t2.getClass().getSimpleName());
System.out.println(" " + t3.getClass().getSimpleName());
// Checking relationships
Exception ex = new RuntimeException();
System.out.println("\nRuntimeException is an Exception: " +
(ex instanceof Exception));
RuntimeException rex = new NullPointerException();
System.out.println("NullPointerException is RuntimeException: " +
(rex instanceof RuntimeException));
// This shows inheritance in action!
System.out.println("\nInheritance chain for NullPointerException:");
System.out.println(" NullPointerException");
System.out.println(" RuntimeException");
System.out.println(" Exception");
System.out.println(" Throwable");
System.out.println(" Object");
}
}
// Output:
// All are Throwable:
// Exception
// RuntimeException
// Error
//
// RuntimeException is an Exception: true
// NullPointerException is RuntimeException: true
//
// Inheritance chain for NullPointerException:
// NullPointerException
// → RuntimeException
// → Exception
// → Throwable
// → Object

✅ Checked vs Unchecked Exceptions

CheckedVsUnchecked.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import java.io.FileReader;
import java.io.IOException;
public class CheckedVsUnchecked {
public static void main(String[] args) {
System.out.println("=== Unchecked Exceptions ===");
demonstrateUnchecked();
System.out.println("\n=== Checked Exceptions ===");
demonstrateChecked();
}
// UNCHECKED: No need to declare or catch
public static void demonstrateUnchecked() {
// These are RuntimeException subclasses
// No try-catch or throws required!
try {
// NullPointerException (unchecked)
String text = null;
System.out.println(text.length());
} catch (NullPointerException e) {
System.out.println("NPE: " + e.getClass().getSimpleName());
}
try {
// ArrayIndexOutOfBoundsException (unchecked)
int[] arr = {1, 2, 3};
System.out.println(arr[10]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("AIOOB: " + e.getClass().getSimpleName());
}
try {
// ArithmeticException (unchecked)
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Arithmetic: " + e.getClass().getSimpleName());
}
try {
// IllegalArgumentException (unchecked)
Thread.sleep(-1);
} catch (IllegalArgumentException e) {
System.out.println("IllegalArg: " + e.getClass().getSimpleName());
} catch (InterruptedException e) {
// This is checked!
}
}
// CHECKED: MUST declare with throws or catch
public static void demonstrateChecked() {
// IOException - MUST be handled!
try {
FileReader reader = new FileReader("file.txt");
// If we don't catch, we must add: throws IOException
} catch (IOException e) {
System.out.println("IOException: " + e.getClass().getSimpleName());
}
// InterruptedException - MUST be handled!
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("Interrupted: " + e.getClass().getSimpleName());
}
}
}
// Key differences:
// CHECKED (Exception, but NOT RuntimeException):
// - Compiler forces you to handle them
// - Use throws or try-catch
// - Examples: IOException, SQLException
//
// UNCHECKED (RuntimeException and Error):
// - Optional to handle
// - Usually programming errors
// - Examples: NullPointerException, ArrayIndexOutOfBoundsException
// Output:
// === Unchecked Exceptions ===
// NPE: NullPointerException
// AIOOB: ArrayIndexOutOfBoundsException
// Arithmetic: ArithmeticException
// IllegalArg: IllegalArgumentException
//
// === Checked Exceptions ===
// IOException: FileNotFoundException
// Interrupted: InterruptedException

🎯 Catching Parent Classes

CatchingParents.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public class CatchingParents {
public static void main(String[] args) {
System.out.println("=== Test 1: Specific Catch ===");
test1();
System.out.println("\n=== Test 2: Parent Catch ===");
test2();
System.out.println("\n=== Test 3: Generic Catch ===");
test3();
}
// Catching specific exception
public static void test1() {
try {
String text = null;
System.out.println(text.length());
} catch (NullPointerException e) {
// Catches only NullPointerException
System.out.println("Caught specific: " + e.getClass().getSimpleName());
}
}
// Catching parent class
public static void test2() {
try {
String text = null;
System.out.println(text.length());
} catch (RuntimeException e) {
// Catches NullPointerException (child of RuntimeException)
System.out.println("Caught parent: " + e.getClass().getSimpleName());
System.out.println(" (using RuntimeException catch)");
}
}
// Catching top-level Exception
public static void test3() {
try {
String text = null;
System.out.println(text.length());
} catch (Exception e) {
// Catches ALL exceptions (Exception is parent of all)
System.out.println("Caught generic: " + e.getClass().getSimpleName());
System.out.println(" (using Exception catch)");
}
}
}
// Output:
// === Test 1: Specific Catch ===
// Caught specific: NullPointerException
//
// === Test 2: Parent Catch ===
// Caught parent: NullPointerException
// (using RuntimeException catch)
//
// === Test 3: Generic Catch ===
// Caught generic: NullPointerException
// (using Exception catch)
// Important: Parent catch blocks catch all child exceptions!
// NullPointerException is caught by:
// - catch(NullPointerException)
// - catch(RuntimeException) ← parent
// - catch(Exception) ← grandparent
// - catch(Throwable) ← great-grandparent

⚠️ Catch Order Matters!

CatchOrder.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class CatchOrder {
public static void main(String[] args) {
correctOrder();
// incorrectOrder(); // This won't compile!
}
// ✓ CORRECT: Most specific first
public static void correctOrder() {
try {
String text = null;
System.out.println(text.length());
} catch (NullPointerException e) {
// Specific exception first
System.out.println("Caught NPE");
} catch (RuntimeException e) {
// More general exception second
System.out.println("Caught RuntimeException");
} catch (Exception e) {
// Most general exception last
System.out.println("Caught Exception");
}
}
/* WRONG: This won't compile!
public static void incorrectOrder() {
try {
String text = null;
System.out.println(text.length());
} catch (Exception e) {
// Parent first - THIS IS WRONG!
System.out.println("Caught Exception");
} catch (RuntimeException e) {
// ERROR: Already caught by Exception above!
// Compiler error: "exception has already been caught"
System.out.println("Caught RuntimeException");
} catch (NullPointerException e) {
// ERROR: Already caught by Exception above!
System.out.println("Caught NPE");
}
}
*/
}
// RULE: Catch blocks must go from most specific to most general!
//
// Correct order (child → parent → grandparent):
// catch (NullPointerException) ← most specific
// catch (RuntimeException) ← more general
// catch (Exception) ← most general
//
// Why? Because parent catch blocks catch all child exceptions.
// If you put Exception first, it catches everything,
// making later catch blocks unreachable (compiler error).
// Output:
// Caught NPE

⚡ Common RuntimeExceptions

CommonRuntimeExceptions.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
public class CommonRuntimeExceptions {
public static void main(String[] args) {
demonstrateCommonExceptions();
}
public static void demonstrateCommonExceptions() {
// 1. NullPointerException
try {
String text = null;
text.length();
} catch (NullPointerException e) {
System.out.println("1. NullPointerException");
System.out.println(" Accessing method/field on null object\n");
}
// 2. ArrayIndexOutOfBoundsException
try {
int[] arr = {1, 2, 3};
System.out.println(arr[10]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("2. ArrayIndexOutOfBoundsException");
System.out.println(" Index outside array bounds\n");
}
// 3. ArithmeticException
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("3. ArithmeticException");
System.out.println(" Division by zero\n");
}
// 4. IllegalArgumentException
try {
Thread.sleep(-100);
} catch (IllegalArgumentException e) {
System.out.println("4. IllegalArgumentException");
System.out.println(" Invalid argument passed to method\n");
} catch (InterruptedException e) {}
// 5. NumberFormatException (subclass of IllegalArgumentException)
try {
int num = Integer.parseInt("abc");
} catch (NumberFormatException e) {
System.out.println("5. NumberFormatException");
System.out.println(" Invalid string to number conversion\n");
}
// 6. ClassCastException
try {
Object obj = "Hello";
Integer num = (Integer) obj;
} catch (ClassCastException e) {
System.out.println("6. ClassCastException");
System.out.println(" Invalid type casting\n");
}
// 7. IllegalStateException
try {
java.util.ArrayList<String> list = new java.util.ArrayList<>();
java.util.Iterator<String> it = list.iterator();
it.remove(); // Can't remove before next()
} catch (IllegalStateException e) {
System.out.println("7. IllegalStateException");
System.out.println(" Object in invalid state for operation\n");
}
System.out.println("All RuntimeExceptions are UNCHECKED");
System.out.println("(no need to catch or declare)");
}
}
// Common RuntimeExceptions (all unchecked):
//
// ├── NullPointerException - accessing null reference
// ├── ArrayIndexOutOfBoundsException - invalid array index
// ├── ArithmeticException - math error (divide by zero)
// ├── IllegalArgumentException - invalid method argument
// │ └── NumberFormatException - string to number failed
// ├── ClassCastException - invalid type cast
// ├── IllegalStateException - invalid object state
// └── UnsupportedOperationException - operation not supported

💥 Error Examples (Don't Catch These!)

ErrorExamples.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
public class ErrorExamples {
public static void main(String[] args) {
System.out.println("Common Java Errors (system problems):\n");
demonstrateErrors();
}
public static void demonstrateErrors() {
// Note: These are demonstrations - they would crash in real code!
System.out.println("1. OutOfMemoryError");
System.out.println(" JVM runs out of heap memory");
System.out.println(" Example: Creating too many large objects");
// int[] huge = new int[Integer.MAX_VALUE]; // Would cause this
System.out.println("\n2. StackOverflowError");
System.out.println(" Stack memory exhausted (usually infinite recursion)");
System.out.println(" Example: Method calling itself forever");
// recursiveMethod(); // Would cause this
System.out.println("\n3. NoClassDefFoundError");
System.out.println(" Required class file not found at runtime");
System.out.println("\n4. ExceptionInInitializerError");
System.out.println(" Exception in static initializer block");
System.out.println("\n5. AssertionError");
System.out.println(" Assertion failed (when assertions enabled)");
System.out.println("\n--- Why NOT catch Errors? ---");
System.out.println("• They indicate serious system problems");
System.out.println("• Usually can't recover from them");
System.out.println("• JVM needs to handle them");
System.out.println("• Your application should terminate");
}
// This would cause StackOverflowError
public static void recursiveMethod() {
recursiveMethod(); // Infinite recursion!
}
}
// Error hierarchy (all unchecked):
//
// Error
// ├── VirtualMachineError
// │ ├── OutOfMemoryError - no more heap memory
// │ ├── StackOverflowError - stack memory exhausted
// │ └── InternalError - JVM internal error
// ├── LinkageError
// │ ├── NoClassDefFoundError - class not found
// │ └── ClassFormatError - invalid class file
// ├── AssertionError - assertion failed
// └── ... (other system errors)
// Output:
// Common Java Errors (system problems):
//
// 1. OutOfMemoryError
// JVM runs out of heap memory
// Example: Creating too many large objects
//
// 2. StackOverflowError
// Stack memory exhausted (usually infinite recursion)
// Example: Method calling itself forever
//
// 3. NoClassDefFoundError
// Required class file not found at runtime
//
// 4. ExceptionInInitializerError
// Exception in static initializer block
//
// 5. AssertionError
// Assertion failed (when assertions enabled)
//
// --- Why NOT catch Errors? ---
// • They indicate serious system problems
// • Usually can't recover from them
// • JVM needs to handle them
// • Your application should terminate

🌐 Complete Hierarchy in Action

CompleteHierarchy.java
java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
public class CompleteHierarchy {
public static void main(String[] args) {
demonstrateHierarchy();
}
public static void demonstrateHierarchy() {
// Test 1: Catching specific exception
System.out.println("=== Test 1: Specific Catch ===");
try {
throwNPE();
} catch (NullPointerException e) {
System.out.println("Caught: " + e.getClass().getSimpleName());
}
// Test 2: Catching RuntimeException (parent)
System.out.println("\n=== Test 2: Parent Catch ===");
try {
throwNPE();
} catch (RuntimeException e) {
System.out.println("Caught as: RuntimeException");
System.out.println("Actual type: " + e.getClass().getSimpleName());
}
// Test 3: Catching Exception (grandparent)
System.out.println("\n=== Test 3: Grandparent Catch ===");
try {
throwNPE();
} catch (Exception e) {
System.out.println("Caught as: Exception");
System.out.println("Actual type: " + e.getClass().getSimpleName());
}
// Test 4: Multiple different exceptions
System.out.println("\n=== Test 4: Multiple Exceptions ===");
handleMultipleExceptions(1);
handleMultipleExceptions(2);
handleMultipleExceptions(3);
}
public static void throwNPE() {
throw new NullPointerException("Test NPE");
}
public static void handleMultipleExceptions(int type) {
try {
switch (type) {
case 1:
throw new NullPointerException("NPE");
case 2:
throw new IllegalArgumentException("IllegalArg");
case 3:
throw new ArithmeticException("Arithmetic");
}
} catch (NullPointerException e) {
System.out.println("Specific: " + e.getMessage());
} catch (IllegalArgumentException e) {
System.out.println("Specific: " + e.getMessage());
} catch (RuntimeException e) {
// Catches any other RuntimeException
System.out.println("Generic Runtime: " + e.getMessage());
}
}
}
// Hierarchy Summary:
//
// Object (root of all classes)
// └── Throwable (root of exception hierarchy)
// ├── Error (serious system problems)
// │ ├── OutOfMemoryError
// │ ├── StackOverflowError
// │ └── ... (don't catch these!)
// │
// └── Exception (problems you can handle)
// ├── RuntimeException (unchecked)
// │ ├── NullPointerException
// │ ├── ArrayIndexOutOfBoundsException
// │ ├── ArithmeticException
// │ ├── IllegalArgumentException
// │ │ └── NumberFormatException
// │ └── ...
// │
// └── Checked Exceptions (must handle)
// ├── IOException
// ├── SQLException
// ├── ClassNotFoundException
// └── ...
// Output:
// === Test 1: Specific Catch ===
// Caught: NullPointerException
//
// === Test 2: Parent Catch ===
// Caught as: RuntimeException
// Actual type: NullPointerException
//
// === Test 3: Grandparent Catch ===
// Caught as: Exception
// Actual type: NullPointerException
//
// === Test 4: Multiple Exceptions ===
// Specific: NPE
// Specific: IllegalArg
// Generic Runtime: Arithmetic

🔑 Key Concepts

Checked Exceptions

Must be caught or declared with throws

IOException, SQLException, ClassNotFoundException

Unchecked Exceptions

Optional to catch (RuntimeException subclasses)

NullPointerException, ArrayIndexOutOfBounds

Errors

Serious problems, shouldn't be caught

OutOfMemoryError, StackOverflowError

Inheritance

Child exceptions can be caught by parent catch blocks

catch(Exception e) catches all exceptions

Best Practices

  • Catch specific exceptions before general ones (child before parent)
  • Don't catch Error - let the JVM handle system problems
  • Understand the difference between checked and unchecked
  • Use exception hierarchy to organize your catch blocks
  • Remember: catching Exception catches all exceptions (not errors)
  • Be careful with catch(Throwable) - catches errors too!

💼 Interview Tips

  • Know the hierarchy: Throwable → Error/Exception → RuntimeException
  • Understand checked (must handle) vs unchecked (optional)
  • Remember that Error and RuntimeException are unchecked
  • Know common exceptions: NPE, AIOOB, IllegalArgument, etc.
  • Understand why catch order matters (most specific first)
  • Be able to explain when to extend Exception vs RuntimeException