Exception Chaining and Return Statement Flow
Key Rule: Exceptions thrown in catch blocks are NOT caught by subsequent catch blocks in the same try-catch structure. Only exceptions from the try block can be caught.
π Exception Hierarchy Reminder:
java.lang.Throwable
βββ java.lang.Error (unchecked - JVM errors, don't catch)
βββ java.lang.Exception
βββ java.lang.RuntimeException (unchecked - programming errors)
β βββ IllegalArgumentException
β βββ NullPointerException
β βββ IndexOutOfBoundsException
β βββ IllegalStateException
βββ Checked Exceptions (must handle or declare)
βββ IOException
βββ SQLException
βββ FileNotFoundException (extends IOException)
βββ ClassNotFoundException
Exception Classification Rules:
- Checked Exception: Any exception that extends
java.lang.Exception
but is NOT a subclass ofjava.lang.RuntimeException
- Unchecked Exception: Any exception that extends
java.lang.RuntimeException
(orjava.lang.Error
) - Checked exceptions must be handled with try-catch OR declared with
throws
- Unchecked exceptions can be handled but donβt have to be
public class FamilyReunion {
public static void main(String[] args) {
try {
callGrandpa(); // This will throw an exception from TRY block
} catch (IndexOutOfBoundsException father) {
System.out.println("Father"); // Step 2: Catches exception from try block
throw new NullPointerException(); // Step 3: Throws NEW exception from CATCH block
} catch (NullPointerException mother) {
System.out.println("Mother"); // β WON'T EXECUTE! Exception not from try block
return;
} catch (Exception grandma) {
System.out.println("Grandma"); // β WON'T EXECUTE! Exception not from try block
} finally {
System.out.println("Sister"); // Step 4: Always executes
}
System.out.println("FAMILY_GATHERING"); // β WON'T EXECUTE! NullPointerException propagates
}
static void callGrandpa() {
System.out.println("Grandpa speaks"); // Step 1: Always executes first
throw new IndexOutOfBoundsException("Grandpa is grumpy!");
}
}
// Output:
// Grandpa speaks
// Father
// Sister
// Exception in thread "main" java.lang.NullPointerException
Step-by-Step Execution Flow:
// Detailed execution breakdown:
public class FamilyReunion {
public static void main(String[] args) {
try {
callGrandpa(); // Step 1: Call method - throws IndexOutOfBoundsException
} catch (IndexOutOfBoundsException father) {
System.out.println("Father"); // Step 2: Catches exception FROM TRY BLOCK
throw new NullPointerException(); // Step 3: NEW exception FROM CATCH BLOCK
} catch (NullPointerException mother) { // β CANNOT catch exceptions from catch blocks!
System.out.println("Mother"); // NEVER executes - exception not from try block
return;
} catch (Exception grandma) { // β CANNOT catch exceptions from catch blocks!
System.out.println("Grandma"); // NEVER executes - exception not from try block
} finally {
System.out.println("Sister"); // Step 4: Always executes before exception propagates
}
System.out.println("FAMILY_GATHERING"); // NEVER executes - uncaught exception terminates method
}
static void callGrandpa() {
System.out.println("Grandpa speaks"); // Step 1a: Method execution
throw new IndexOutOfBoundsException("Grandpa is grumpy!"); // Step 1b: Exception FROM TRY BLOCK
}
}
π¨ Critical Concept: Only exceptions thrown in the TRY block can be caught by catch blocks!
Exception Source Matters:
try {
throw new IOException("From try block"); // β
CAN be caught by catch blocks below
} catch (IOException e) {
System.out.println("Caught from try"); // β
Executes - exception from try block
throw new SQLException("From catch block"); // β CANNOT be caught by catch blocks below
} catch (SQLException e) {
System.out.println("Caught from catch"); // β NEVER executes - exception from catch block
} catch (Exception e) {
System.out.println("Caught anything"); // β NEVER executes - exception from catch block
}
// SQLException propagates up - uncaught!
Key Exception Catching Rules:
β Exceptions that CAN be caught:
try {
throw new RuntimeException("From try block"); // β
From try block
methodThatThrows(); // β
From method called in try block
} catch (RuntimeException e) {
// Will catch exceptions from try block
}
β Exceptions that CANNOT be caught:
try {
normalMethod(); // No exception from try block
} catch (IOException e) {
throw new SQLException("From catch block"); // β From catch block - won't be caught
} catch (SQLException e) {
// NEVER executes - SQLException came from catch block
} finally {
throw new IllegalStateException("From finally"); // β From finally block - won't be caught
}
Nested Try-Catch Example:
// To catch exceptions from catch blocks, you need NESTED try-catch:
try {
try {
throw new IOException("Original problem");
} catch (IOException e) {
System.out.println("Caught original");
throw new SQLException("New problem"); // This gets thrown to outer try
}
} catch (SQLException e) { // β
This CAN catch it - different try block!
System.out.println("Caught from inner catch");
}
π‘ Learning Tips:
- Try block rule: βOnly try block exceptions are catchableβ - catch blocks canβt catch exceptions from other catch blocks
- Finally guarantee: βFinally runs then exception propagatesβ - finally executes but doesnβt catch exceptions
- Propagation path: βUp and outβ - uncaught exceptions from catch/finally blocks propagate to calling method
- Nested solution: βTry within tryβ - use nested try-catch to handle exceptions from catch blocks
Common Exam Traps:
- Catch block exceptions - Thinking subsequent catch blocks will handle exceptions thrown in earlier catch blocks
- Finally block exceptions - Assuming finally can catch its own exceptions
- Exception propagation - Not realizing uncaught exceptions terminate the method
- Nested try confusion - Not understanding when you need nested try-catch structures
Q: Can a catch block catch an exception thrown by another catch block in the same try-catch structure?
A: No! Only exceptions thrown in the try block can be caught by catch blocks. Exceptions from catch blocks propagate up uncaught.