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.
/* * 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:
Throwable
The root of all exceptions and errors (great-grandparent)
Error
Serious system problems (usually can't recover)
Exception
Problems your code can catch and handle
RuntimeException
Unchecked exceptions (optional to catch)
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
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
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!
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
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!)
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
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