Exception handling is all about planning for failures. In Java, a single block of code can potentially throw multiple types of exceptions. To handle this, we chain multiple catch blocks. Furthermore, Java guarantees that a finally block executes no matter what—except under one critical trap.

In this guide, we will trace how multiple catches execute, and examine when the "always executed" finally block is bypassed.

Illustration of cascading catch nets and a finally gate with a stop sign representing exit execution
Real-World Analogy: Sorting Funnels and the Janitor

Imagine dropping balls down a series of funnels:

  • Multiple Catch Blocks: The top funnel filters out specific red balls (ArithmeticException). The middle funnel catches blue balls (ArrayIndexOutOfBoundsException). The bottom wide funnel catches all remaining balls (generic Exception). If you place the wide funnel at the top, it swallows everything, rendering the specific ones useless.
  • The finally Block: At the end of the day, no matter what balls fell down the funnels, the janitor sweeps the floor (cleanup operations). The only way they don't clean is if someone pulls the building's fire alarm and forces everyone to evacuate immediately (`System.exit()`).

1. Chaining Multiple Catch Blocks

When write multiple catch blocks, Java checks them sequentially from top to bottom. You must always place specific exceptions above parent exceptions:

public class MulCatch {
    public static void main(String args[]) {
        try {
            int a[] = new int[5];
            a[1] = 30 / 0; // Triggers ArithmeticException first
        } catch (ArithmeticException e) {
            System.out.println("task1 is completed : " + e);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("task 2 completed : " + e);
        } catch (Exception e) { // Must go last!
            System.out.println("common task completed");
        }
    }
}

If you put the generic catch (Exception e) at the top, the compiler throws a "Variable already defined / Unreachable catch block" error because all exceptions extend Exception, making lower blocks unreachable.

2. The finally Block and the System.exit() Trap

Developers are taught that the code inside the finally block executes regardless of whether an exception occurs or is caught. However, look at this exception handler:

public class FinalExample {
    public static void main(String args[]) {
        try {
            int data = 25 / 0;
            System.out.println(data);
        } catch (ArithmeticException e) {
            System.out.println("Exception caught: " + e);
            System.exit(0); // TRAP: Immediately halts the JVM!
        } finally {
            System.out.println("finally block is always executed"); // DOES NOT RUN!
        }
    }
}

Normally, the finally block runs right after the catch block finishes. But because we called System.exit(0), the JVM is terminated immediately. The system halts, and the console never prints the finally statement!

Conclusion

When design try-catch blocks:

  • Order catch blocks from most specific subclasses to most generic parent classes.
  • Use finally for releasing file handles, closing sockets, or db connections.
  • Remember: System.exit(0), system crashes, or power outages will bypass the finally execution.