Back to try-catch-finally
Q3Question 3 of 4

How do multiple catch blocks work?

Key Concepts
  • A single try can have multiple catch blocks, each handling a different exception type
  • Catch blocks are evaluated top to bottom and the first matching catch wins
  • Specific exceptions must come before general ones or the compiler reports unreachable catch block
  • catch(Exception) before catch(IOException) causes a compile error because IOException is already covered by Exception
  • Java 7 multi-catch syntax catch(A | B e) handles multiple unrelated types in one block
  • Multi-catch types must not have a parent-child relationship and the caught variable is implicitly final

Definition

A single try can have multiple catch blocks evaluated top-to-bottom, and specific exceptions must come before general ones because the first matching catch wins.
  • Yes, a single try can have multiple catch blocks, each handling a different exception type.
  • They are checked top-to-bottom: the first catch whose type matches (via IS-A relationship) the thrown exception executes. Only one catch block runs.
  • Specific exceptions must come before general ones. If catch(Exception e) comes first, it catches everything, making subsequent catches like catch(IOException e) unreachable. The compiler rejects this.
  • Java 7 added multi-catch: catch (IOException | SQLException e) handles multiple unrelated exception types in one block. The caught variable is implicitly final (cannot be reassigned). The types must not have a parent-child relationship.

At National Bank of India, there are three complaint counters. Counter 1 handles "insufficient funds" (specific). Counter 2 handles "any transaction error" (general). Counter 3 handles "everything else." If the "everything else" counter is first, nobody reaches the specific counters. Java prevents this by requiring specific counters first, general last.

Understanding the Concepts

How do multiple catch blocks work?

Multiple catch blocks follow a single try block, ordered from most specific to most general. The JVM evaluates them top-to-bottom and executes the first one that matches the thrown exception.

JAVA
package com.bank.demo;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigDecimal;

public class MultipleCatchDemo {

    public static void processFile(int scenario) throws IOException {
        switch (scenario) {
            case 1 -> throw new FileNotFoundException("trades.csv not found");
            case 2 -> throw new IOException("Disk read error");
            case 3 -> throw new IllegalArgumentException("Bad trade format");
            case 4 -> throw new NullPointerException("Null account reference");
        }
        System.out.println("Processed successfully");
    }

    public static void main(String[] args) {
        for (int i = 1; i <= 5; i++) {
            try {
                processFile(i);
            } catch (FileNotFoundException e) {
                System.out.println("Scenario " + i + ": File not found - " + e.getMessage());
            } catch (IOException e) {
                System.out.println("Scenario " + i + ": IO error - " + e.getMessage());
            } catch (RuntimeException e) {
                System.out.println("Scenario " + i + ": Runtime error - " + e.getMessage());
            }
        }
    }
}
TEXT
Scenario 1: File not found - trades.csv not found
Scenario 2: IO error - Disk read error
Scenario 3: Runtime error - Bad trade format
Scenario 4: Runtime error - Null account reference
Processed successfully

Why must specific exceptions come before general ones?

Because catch blocks are checked top-to-bottom and the first match wins. If a general type like Exception comes first, it matches everything, making subsequent catches unreachable. The compiler rejects this as a compile error.

JAVA
package com.bank.demo;

public class CatchOrderDemo {

    // WRONG: general before specific (compile error)
    // public static void wrongOrder() {
    //     try {
    //         throw new java.io.IOException("error");
    //     } catch (Exception e) {           // catches EVERYTHING
    //         System.out.println("General");
    //     } catch (java.io.IOException e) {  // UNREACHABLE!
    //         System.out.println("Specific");
    //     }
    //     // Compiler error: exception IOException has already been caught
    // }

    // CORRECT: specific before general
    public static void correctOrder() {
        try {
            throw new java.io.IOException("Disk error reading SAV2001 statement");
        } catch (java.io.FileNotFoundException e) {
            System.out.println("Most specific: " + e.getMessage());
        } catch (java.io.IOException e) {
            System.out.println("Less specific: " + e.getMessage());
        } catch (Exception e) {
            System.out.println("Catch-all: " + e.getMessage());
        }
    }

    public static void main(String[] args) {
        correctOrder();
    }
}
TEXT
Less specific: Disk error reading SAV2001 statement

What is multi-catch (Java 7+)?

Multi-catch lets a single catch block handle multiple unrelated exception types using the pipe (|) operator. It eliminates duplicate code when two separate catch blocks do the same thing.

JAVA
package com.bank.demo;

import java.io.IOException;
import java.sql.SQLException;

public class MultiCatchDemo {

    // BEFORE Java 7: duplicate catch blocks
    public static void beforeMultiCatch() {
        try {
            throw new IOException("File error");
        } catch (IOException e) {
            System.out.println("Old: export failed - " + e.getMessage());
        } catch (RuntimeException e) {
            System.out.println("Old: export failed - " + e.getMessage());  // DUPLICATE!
        }
    }

    // AFTER Java 7: multi-catch eliminates duplication
    public static void afterMultiCatch() {
        try {
            throw new IOException("File error");
        } catch (IOException | RuntimeException e) {
            System.out.println("New: export failed - " + e.getMessage());
            // e is implicitly final in multi-catch
            // e = new IOException("x");  // COMPILE ERROR: e is final
        }
    }

    public static void main(String[] args) {
        beforeMultiCatch();
        afterMultiCatch();
    }
}
TEXT
Old: export failed - File error
New: export failed - File error

What are the rules for multi-catch?

The types in a multi-catch must be unrelated (no parent-child relationship), and the caught variable is implicitly final.

JAVA
package com.bank.demo;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;

public class MultiCatchRulesDemo {

    public static void main(String[] args) {
        // VALID: unrelated exception types
        try {
            throw new IOException("test");
        } catch (IOException | SQLException e) {
            System.out.println("Valid multi-catch: " + e.getClass().getSimpleName());
        }

        // INVALID: parent-child relationship
        // catch (IOException | FileNotFoundException e) { }  // COMPILE ERROR!
        // FileNotFoundException IS-A IOException, already caught by IOException.

        // Multi-catch variable is implicitly final:
        try {
            throw new IOException("test");
        } catch (IOException | SQLException e) {
            // e = new IOException("replaced");  // COMPILE ERROR: final
            System.out.println("Cannot reassign e: " + e.getMessage());
        }

        // Single catch variable is NOT final:
        try {
            throw new IOException("original");
        } catch (IOException e) {
            e = new IOException("replaced");  // Allowed in single catch
            System.out.println("Reassigned in single catch: " + e.getMessage());
        }
    }
}
TEXT
Valid multi-catch: IOException
Cannot reassign e: test
Reassigned in single catch: replaced

What are common mistakes with multiple catch blocks?

JAVA
package com.bank.demo;

public class CatchMistakesDemo {

    // WRONG: catching Exception first makes all others unreachable
    // try { ... }
    // catch (Exception e) { }      // catches everything
    // catch (IOException e) { }    // COMPILE ERROR: unreachable

    // WRONG: catching Throwable hides Errors
    // try { ... }
    // catch (Throwable t) { }      // catches OOM, SOE, and all Exceptions

    // CORRECT: specific to general ordering
    public static void correctPattern() {
        try {
            throw new IllegalArgumentException("Bad account: SAV-INVALID");
        } catch (IllegalArgumentException e) {
            System.out.println("1. Most specific: " + e.getMessage());
        } catch (RuntimeException e) {
            System.out.println("2. Less specific: " + e.getMessage());
        } catch (Exception e) {
            System.out.println("3. Catch-all: " + e.getMessage());
        }
    }

    public static void main(String[] args) {
        correctPattern();
    }
}
TEXT
1. Most specific: Bad account: SAV-INVALID

What should a senior engineer know?

  • Bytecode efficiency. Multi-catch compiles to a single handler that multiple exception-table rows point to. This is slightly more efficient than separate catch blocks with identical bodies.
  • Pattern matching for switch (Java 21). Modern Java can switch on exception types, but catch blocks remain the standard mechanism for exception handling.
  • @ExceptionHandler in Spring. Spring's global handler resolves exception types using IS-A matching, similar to catch block ordering. More specific handlers take priority.

What are common interview follow-up questions?

  • How many catch blocks can follow a single try? (No limit, but must be ordered correctly.)
  • Can you catch the same exception type twice? (No, the second is unreachable.)
  • Why is the multi-catch variable final? (Compiler cannot know the concrete type, so reassignment would be unsafe.)
  • Can multi-catch have more than two types? (catch (A | B | C e) is valid.)

Visual Overview

DIAGRAM
  try block throws exception
         │
         ▼
  ┌─────────────────────────────┐
  │ catch (FileNotFound e)      │ ← Check first (most specific)
  │   IS-A match? ──── yes ──▶ EXECUTE this catch
  │                    no       │
  └──────────┬──────────────────┘
             ▼
  ┌─────────────────────────────┐
  │ catch (IOException e)       │ ← Check second
  │   IS-A match? ──── yes ──▶ EXECUTE this catch
  │                    no       │
  └──────────┬──────────────────┘
             ▼
  ┌─────────────────────────────┐
  │ catch (Exception e)         │ ← Check last (most general)
  │   IS-A match? ──── yes ──▶ EXECUTE this catch
  └─────────────────────────────┘

  Only ONE catch block executes.
  First match wins.

Key Takeaway

A single try block can have multiple catch blocks, each handling a different exception type. They are evaluated top-to-bottom, and the first matching catch wins based on the IS-A relationship. This means specific exceptions must come before general ones. If catch(Exception e) is first, it catches everything, making subsequent catch blocks unreachable. The compiler enforces this ordering and produces a compile error for unreachable catches.

Java 7 multi-catch catch (IOException | SQLException e) handles multiple unrelated exception types in one block, eliminating duplicate code. The caught variable is implicitly final (cannot be reassigned), and the types must not have a parent-child relationship. catch (IOException | FileNotFoundException e) is a compile error because FileNotFoundException IS-A IOException.

In the trade processing service at National Bank of India, Rajesh orders catch blocks by specificity: TradeNotFoundException (404) first, then DataAccessException (503) second, then Exception (500) last. This ensures each exception type gets the correct HTTP status code and error message through the global handler.

Remember one sentence: catch blocks are checked top-to-bottom with specific types first and general types last, because the first IS-A match wins.

---

Share
Use ← → arrow keys to navigate