Skip to content
Java exceptions 6 min read

Multiple catch Blocks

A single operation can fail in more than one way. Java lets you attach several catch blocks to one try block so you can handle each failure type differently — logging one, retrying another, and presenting a friendly message for a third. Getting this right keeps your error handling both precise and readable.

Why Multiple catch Blocks?

When your code throws different exceptions, a single generic catch clause often forces you to treat every failure the same way. Multiple catch blocks let you react specifically to each situation.

public class FileDemo {
    public static void main(String[] args) {
        try {
            int[] numbers = new int[5];
            numbers[10] = 42;                        // ArrayIndexOutOfBoundsException
            int result = 10 / 0;                     // ArithmeticException (never reached)
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Array index problem: " + e.getMessage());
        } catch (ArithmeticException e) {
            System.out.println("Math error: " + e.getMessage());
        }
    }
}

Output:

Array index problem: Index 10 out of bounds for length 5

Java evaluates each catch clause top to bottom and runs only the first one that matches. The remaining blocks are skipped.

The Order of catch Blocks Matters

You must place more specific (child) exception types before more general (parent) types. If a parent class appears first, it swallows every subclass — and the compiler won’t let you put a more specific type after it.

// WRONG — compile error: ArithmeticException has already been caught
try {
    int x = 10 / 0;
} catch (Exception e) {           // parent first
    System.out.println("General");
} catch (ArithmeticException e) { // unreachable — compiler error!
    System.out.println("Arithmetic");
}

The correct order is most-specific first:

// CORRECT
try {
    int x = 10 / 0;
} catch (ArithmeticException e) { // specific first
    System.out.println("Arithmetic: " + e.getMessage());
} catch (Exception e) {           // general fallback
    System.out.println("Something else: " + e.getMessage());
}

Output:

Arithmetic: / by zero

Warning: Catching Exception or Throwable as the only or first block is a code smell. It hides bugs and makes debugging painful. Always prefer specific types; use a general catch only as a last-resort fallback.

A Realistic Multi-Exception Example

Here is a method that can fail in three distinct ways, each handled differently:

import java.io.*;

public class DataProcessor {
    public static void process(String filename, String indexStr) {
        try {
            // Could throw NumberFormatException
            int index = Integer.parseInt(indexStr);

            // Could throw FileNotFoundException
            FileReader reader = new FileReader(filename);

            // Could throw ArrayIndexOutOfBoundsException
            String[] data = {"alpha", "beta", "gamma"};
            System.out.println("Value: " + data[index]);

        } catch (NumberFormatException e) {
            System.out.println("Bad index input — not a number: " + e.getMessage());
        } catch (FileNotFoundException e) {
            System.out.println("File not found: " + filename);
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Index out of range: " + e.getMessage());
        }
    }

    public static void main(String[] args) {
        process("data.txt", "abc");   // NumberFormatException
        process("missing.txt", "1"); // FileNotFoundException
        process("any.txt", "99");    // ArrayIndexOutOfBoundsException
    }
}

Tip: Each catch block receives its own e variable — a fresh reference to the thrown exception object. You can call e.getMessage(), e.getClass().getName(), or e.printStackTrace() inside any of them.

Multi-Catch: One Block for Multiple Types (Java 7+)

Java 7 introduced the multi-catch syntax — a single catch block that handles several unrelated exception types. Separate them with a pipe (|).

public class MultiCatchDemo {
    public static void main(String[] args) {
        try {
            String s = null;
            s.length();                  // NullPointerException
        } catch (NullPointerException | IllegalArgumentException e) {
            // one handler for both types
            System.out.println("Caught: " + e.getClass().getSimpleName());
        }
    }
}

Output:

Caught: NullPointerException

Use multi-catch when you want to apply identical logic to multiple unrelated exception types. It avoids duplicating the same handler code in separate blocks.

Note: The exception variable in a multi-catch block is implicitly final. You cannot reassign e inside the block. This is intentional — the compiler needs to know the exact type at compile time to verify bytecode.

When to Use Multi-Catch vs Separate Blocks

SituationUse
Different recovery logic per exceptionSeparate catch blocks
Same logic, unrelated exception typesMulti-catch (|)
One exception is a subtype of anotherSeparate blocks, child first
Logging only, then re-throwMulti-catch is clean

Catching a Parent Exception Class

You can catch any parent in the hierarchy to handle a whole family of exceptions. This is useful for library boundaries where you want to catch all IOException subtypes:

import java.io.*;

public class IoDemo {
    public static void readFile(String path) {
        try {
            FileReader fr = new FileReader(path);
            BufferedReader br = new BufferedReader(fr);
            System.out.println(br.readLine());
            br.close();
        } catch (FileNotFoundException e) {
            System.out.println("File missing: " + path);
        } catch (IOException e) {
            // catches all other IOException subtypes
            System.out.println("I/O error: " + e.getMessage());
        }
    }
}

FileNotFoundException is a subtype of IOException, so it must come first. The second block acts as a catch-all for any other I/O failure.

Under the Hood

When the JVM throws an exception it creates an exception object on the heap and places a reference to it on the operand stack. The JVM then walks the exception table embedded in the class file — a list of [start_pc, end_pc, handler_pc, catch_type] entries generated by the compiler for every catch block.

For multiple catch blocks the compiler emits multiple rows in the exception table for the same try range, each pointing to a different handler PC and a different catch_type constant pool entry. The JVM checks them in order; the first row whose catch_type is assignable from the thrown exception wins. This is why order matters at the source level — it maps directly to row order in the exception table.

Multi-catch (A | B) compiles to a single exception table entry with a handler that checks the type with instanceof internally, producing slightly smaller bytecode than two identical handlers and making it clearer to the reader that the logic is shared. You can inspect the exception table yourself with the javap -c -verbose command.

Tip: Exception handling has virtually zero runtime cost on the happy path (no exception thrown) in modern JVMs — the exception table is consulted only when an exception actually occurs.

Common Mistakes to Avoid

  • Catching Exception too early — masks bugs like NullPointerException that you should fix, not hide.
  • Empty catch blocks — silently swallowing exceptions makes debugging nearly impossible. At minimum, log the exception.
  • Wrong order (parent before child) — a compile-time error when types are in the same hierarchy, but a logic bug if you use unrelated parent types.
  • Duplicating handler code — if two separate catch blocks do the exact same thing, collapse them into a multi-catch.
// Bad: empty catch
try {
    riskyOperation();
} catch (IOException e) {
    // do nothing — terrible idea
}

// Better: at least log it
try {
    riskyOperation();
} catch (IOException e) {
    System.err.println("IO failed: " + e.getMessage());
}
Last updated June 13, 2026
Was this helpful?