Blog |

How to Catch Multiple Exceptions in Java

How to Catch Multiple Exceptions in Java
Table of Contents

Your Java code was humming along smoothly until... BAM! An exception. No problem, you caught it. But wait, there's another. And another. Suddenly, you're juggling exceptions like you’re in the circus. Don't worry, Java's got your back.

Java offers three ways to catch multiple exceptions: using multiple catch blocks for different exception types, the multi-catch feature to handle multiple exceptions in a single block, and a catch-all block for general exception handling. Let’s look in depth at each.

Use multiple catch blocks

Multiple catch blocks allow you to handle different types of exceptions separately. Here's a simple example:

try {
    // code that may throw exceptions
} catch (IOException e) {
    // handle IOException
} catch (SQLException e) {
    // handle SQLException
}

Let’s take a look at a runnable example:

public class MultipleExceptionExample {
    public static void main(String[] args) {
        try {
        int[] numbers = {1, 2, 3};
        System.out.println(numbers[4]); // Throws ArrayIndexOutOfBoundsException
        int result = 10 / 0;            // This line is never reached
    } catch (ArrayIndexOutOfBoundsException e) {
        System.out.println("Array index out of bounds");
    } catch (ArithmeticException e) {
        System.out.println("Arithmetic error");
    }
    System.out.println("After try-catch block");
    }
}

This is what happens when you run that:

  1. The numbers[4] line throws an ArrayIndexOutOfBoundsException.
  2. Java immediately jumps to the first matching catch block.
  3. The ArrayIndexOutOfBoundsException is caught and handled.
  4. The rest of the try block (i.e. int result = 10 / 0;) is skipped.
  5. Execution continues after all the catch blocks with "After try-catch block".

Order matters here. Arrange your catch blocks from most specific to most general. Java uses the first matching catch block, so if you were to put Exception first that would catch everything.

If you handle multiple exceptions in the same way, consider using the multi-catch feature available in Java 7+ instead.

Use multi-catch syntax (Java 7+)

Java 7 introduced the multi-catch feature, allowing you to handle multiple exception types in a single catch block. This can lead to more concise and readable code, especially when you want to handle different exceptions in the same way.

Here's a simple example:

try {
    // code that may throw exceptions
} catch (IOException | SQLException e) {
    // handle both IOException and SQLException
}

And a longer, runnable example:

public void performOperations(String filename, String sqlQuery) {
    try {
        readFile(filename);
        executeQuery(sqlQuery);
    } catch (IOException | SQLException | IllegalArgumentException e) {
        System.err.println("Operation failed: " + e.getMessage());
        e.printStackTrace();
    }
}

private void readFile(String filename) throws IOException {
    // File reading operation
}

private void executeQuery(String sqlQuery) throws SQLException {
    // Database query execution
}

You use the pipe symbol | to separate exception types and can catch as many exception types as needed, but keep the catch block focused on exceptions that are truly related. Don't overuse — if exceptions require different handling, use separate catch blocks.

The other thing to know is that the exception variable (typically named e) is implicitly final. This means the variable is treated as if it were declared with the final keyword, even though you don't explicitly write it. You cannot reassign a new value to this variable within the catch block. This prevents potential ambiguity about which exception type is being handled at any given point in the catch block.

try {
    // Some code that might throw exceptions
} catch (IOException | SQLException e) {
    // This is allowed:
    System.out.println(e.getMessage());

    // This would cause a compile-time error:
    // e = new IOException("New exception");  // Error!

    // This is also not allowed:
    // e = null;  // Error!
}

You can also combine multi-catch with traditional single-exception catch blocks:

try {
    riskyOperation();
} catch (IOException | SQLException e) {
    System.err.println("IO or SQL error: " + e.getMessage());
} catch (RuntimeException e) {
    System.err.println("Runtime error: " + e.getMessage());
}

This approach allows you to handle some exceptions together while still providing specialized handling for others.

What you cannot do is use multi-catch with exception types that are in a subclass relationship. For example, this will not compile:

try {
    // Code
} catch (IOException | Exception e) { // Compile error
    // Handle exception
}

This is because IOException is a subclass of Exception, making the multi-catch redundant.

Use a catch-all block

A catch-all block is a powerful tool in Java's exception handling arsenal, allowing you to catch any exception that wasn't caught by more specific catch blocks. It uses the base Exception class to catch all possible exceptions.

Here’s the basic syntax:

try {
    // code that may throw exceptions
} catch (Exception e) {
    // handle any exception
}

And a runnable example with multiple operations:

public void performComplexOperation() {
    try {
        readFromFile();
        processData();
        writeToDatabase();
    } catch (IOException e) {
        System.err.println("IO error: " + e.getMessage());
    } catch (SQLException e) {
        System.err.println("Database error: " + e.getMessage());
    } catch (Exception e) {
        System.err.println("An unexpected error occurred: " + e.getMessage());
        e.printStackTrace();
    }
}

Always place the catch-all block last. It should come after all more specific exception handlers.

Generally you want to use catch-all blocks sparingly. It may catch exceptions you didn't anticipate and potentially hide serious issues and make debugging more difficult.

Also consider rethrowing the exception using the throw e; syntax. Since you can't handle it, you might want to let it propagate. Here’s an example:

public void performComplexOperation() throws IOException, SQLException, Exception {
        try {
            readFromFile();
            processData();
            writeToDatabase();
        } catch (IOException e) {
            System.err.println("IO error: " + e.getMessage());
            throw e; // Rethrow IOException
        } catch (SQLException e) {
            System.err.println("Database error: " + e.getMessage());
            throw e; // Rethrow SQLException
        } catch (Exception e) {
            System.err.println("An unexpected error occurred: " + e.getMessage());
            e.printStackTrace();
            throw e; // Rethrow any other exception
        }
    }

This allows for more flexible error handling: the performComplexOperation() method can do some initial error handling and logging, while still allowing the caller to decide how to ultimately respond to these errors.

Crucial points to remember

No matter what approach you choose, keep these points in mind.

  • Choose your exception handling approach based on your specific needs. Sometimes multiple catch blocks are best; other times, multi-catch or a catch-all block might be more appropriate.
  • Always catch more specific exceptions before general ones. This allows for more precise error handling and prevents unintended exception masking.
  • While catch-all blocks can be a safety net, overusing them can hide important issues and make debugging more difficult.
  • Proper logging in catch blocks can be invaluable for debugging and monitoring. Include relevant details but be mindful of sensitive information.
  • Sometimes the best action is to log the exception and rethrow it, allowing higher-level code to handle it appropriately.
  • Thoroughly test your exception handling code. Ensure that your application behaves correctly under various error conditions.

Track, Analyze and Manage Errors With Rollbar

Managing errors and exceptions in your code is challenging. It can make deploying production code an unnerving experience. Being able to track, analyze, and manage errors in real-time can help you to proceed with more confidence. Rollbar automates error monitoring and triaging, making fixing Java errors easier than ever. Try it today!

Related Resources

"Rollbar allows us to go from alerting to impact analysis and resolution in a matter of minutes. Without it we would be flying blind."

Error Monitoring

Start continuously improving your code today.

Get Started Shape