Definition
- Yes, a single
trycan 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 likecatch(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 implicitlyfinal(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.
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());
}
}
}
}
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.
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();
}
}
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.
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();
}
}
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.
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());
}
}
}
Valid multi-catch: IOException
Cannot reassign e: test
Reassigned in single catch: replaced
What are common mistakes with multiple catch blocks?
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();
}
}
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
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.
---