A single operation can sometimes fail in more than one way. For example, a program that reads user input and performs a calculation might encounter invalid numeric data, division by zero, or another runtime problem. Java allows you to handle these different situations with multiple catch blocks.
The idea is simple: one try block can be followed by several catch blocks, with each handler responsible for a different exception type.
Why Multiple catch Blocks Exist
Suppose an application accepts a number as text and then divides another number by it. Two different problems can occur: the text might not represent a valid integer, or the resulting integer might be zero.
String input = "0";
try {
int divisor = Integer.parseInt(input);
int result = 100 / divisor;
System.out.println(result);
} catch (NumberFormatException e) {
System.out.println("Please enter a valid number.");
} catch (ArithmeticException e) {
System.out.println("The divisor cannot be zero.");
}
The two exceptions require different responses, so separate catch blocks make the program's intention clear.
Basic Syntax
try {
// Code that may throw different exceptions
} catch (FirstExceptionType e) {
// Handle first exception
} catch (SecondExceptionType e) {
// Handle second exception
} catch (ThirdExceptionType e) {
// Handle third exception
}
Java examines the catch blocks from top to bottom and executes the first compatible handler. This ordering becomes especially important when the exception types are related through inheritance.
How Multiple catch Works
Consider the following example:
try {
int[] numbers = {10, 20, 30};
System.out.println(numbers[5]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Invalid array index.");
} catch (ArithmeticException e) {
System.out.println("Arithmetic problem.");
}
The array access throws ArrayIndexOutOfBoundsException. Java checks the first catch block, finds a compatible exception type, and executes it. The second catch block is skipped.
Remember: For one thrown exception, Java executes only one matching catch block from a single try-catch structure: the first compatible handler.
Execution Flow
try block
|
Exception occurs
|
v
Check first catch
|
+------+------+
| |
Match No match
| |
v v
Execute it Check next catch
|
v
Continue checking
If none of the catch blocks matches the thrown exception, the exception is not handled by that structure and may propagate to the calling method.
Example: Different Exceptions
public class MultipleCatchDemo {
public static void main(String[] args) {
String text = "abc";
try {
int number = Integer.parseInt(text);
int result = 100 / number;
System.out.println(result);
} catch (NumberFormatException e) {
System.out.println("Invalid numeric input.");
} catch (ArithmeticException e) {
System.out.println("Cannot divide by zero.");
}
}
}
In this example, the first operation attempts to convert abc into an integer. That conversion fails, so NumberFormatException is thrown and its catch block executes.
The division is never reached because an exception interrupted the try block earlier.
Only One Catch Executes
A common beginner misunderstanding is expecting all matching catch blocks to execute. They do not.
try {
int value = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Arithmetic exception.");
} catch (RuntimeException e) {
System.out.println("Runtime exception.");
}
Both catch types are technically compatible because ArithmeticException is a subclass of RuntimeException. However, only the first matching handler executes. The second catch block is skipped.
Catch Order Matters
The inheritance relationship between exception classes makes catch ordering important. A specific child exception must appear before its broader parent exception.
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Arithmetic problem.");
} catch (RuntimeException e) {
System.out.println("Runtime problem.");
}
This is valid because ArithmeticException is more specific than RuntimeException.
Incorrect Catch Order
try {
int result = 10 / 0;
} catch (RuntimeException e) {
System.out.println("Runtime problem.");
} catch (ArithmeticException e) {
System.out.println("Arithmetic problem.");
}
This arrangement is invalid because RuntimeException can already catch an ArithmeticException. The second catch block can therefore never be reached.
Important: Always place more specific exception types before their parent types. Think of the order as moving from narrow handling to broad fallback handling.
Specific to General
| Order | Exception Type | Role |
|---|---|---|
| 1 | NumberFormatException | Handles invalid numeric text. |
| 2 | ArithmeticException | Handles invalid arithmetic. |
| 3 | RuntimeException | Broader runtime fallback. |
| 4 | Exception | Very broad application-level fallback. |
Multiple catch with Different Recovery Logic
The real advantage of multiple catch blocks is that each exception can receive a response appropriate to its cause.
try {
String input = "100";
int value = Integer.parseInt(input);
int result = 500 / value;
System.out.println("Result: " + result);
} catch (NumberFormatException e) {
System.out.println("Input must contain digits.");
} catch (ArithmeticException e) {
System.out.println("Calculation cannot use zero.");
}
This is more useful than a single generic message because the program can tell the user or calling layer exactly what needs to be corrected.
Multiple catch and Inheritance
Suppose a try block can produce several related runtime exceptions. You can handle specific cases individually and then provide a broader fallback.
try {
// Risky operation
} catch (NumberFormatException e) {
System.out.println("Invalid number.");
} catch (IllegalArgumentException e) {
System.out.println("Invalid argument.");
} catch (RuntimeException e) {
System.out.println("Unexpected runtime problem.");
}
This structure follows the inheritance hierarchy from more specific types to broader types. It gives the application precise handling first and general handling only when necessary.
Multi-Catch: Handling Several Exceptions Together
Java also provides a special feature called multi-catch. When several exception types require exactly the same handling logic, they can be listed in a single catch block using the vertical bar character.
try {
// Risky operations
} catch (IOException | SQLException e) {
System.out.println("A data access problem occurred.");
}
This avoids repeating identical catch blocks when the recovery strategy is the same.
Why Multi-Catch Is Useful
Imagine that two unrelated exceptions both mean that an operation cannot continue and both should produce the same response. Writing two identical handlers adds unnecessary duplication.
try {
// Operation
} catch (IOException e) {
System.out.println("Operation failed.");
} catch (SQLException e) {
System.out.println("Operation failed.");
}
Multi-catch can express the same intention more compactly.
try {
// Operation
} catch (IOException | SQLException e) {
System.out.println("Operation failed.");
}
Important Multi-Catch Restriction
The exception types listed in a multi-catch must not have a parent-child relationship. Java does not allow redundant alternatives such as a child exception together with its parent.
try {
// Operation
} catch (IOException | Exception e) {
// Invalid
}
The reason is straightforward: Exception already includes IOException, so listing both would be redundant.
Multi-Catch Parameter
The variable in a multi-catch block represents an exception whose type is one of the listed alternatives.
try {
// Operation
} catch (IOException | SQLException e) {
System.out.println(e.getMessage());
}
The handler can use members that are available through the common type relationship shared by the alternatives. This is another reason multi-catch is best suited to exceptions that genuinely require the same handling strategy.
A Practical Example
public class InputProcessor {
public static void main(String[] args) {
String value = "abc";
try {
int number = Integer.parseInt(value);
System.out.println(100 / number);
} catch (NumberFormatException e) {
System.out.println("The supplied value is not numeric.");
} catch (ArithmeticException e) {
System.out.println("The numeric value cannot be zero.");
} catch (RuntimeException e) {
System.out.println("An unexpected runtime problem occurred.");
}
}
}
This example demonstrates a useful hierarchy of responsibility. The first two handlers deal with known and specific problems. The final handler acts as a broader fallback for other runtime exceptions.
Common Beginner Mistakes
- Putting a parent exception before its child exception.
- Assuming multiple catch blocks execute for the same exception.
- Using separate catch blocks when all exceptions require identical handling.
- Catching Exception everywhere and losing information about the actual failure.
- Writing different catch blocks with exactly the same code when multi-catch would be clearer.
- Adding many catch blocks without considering whether each one provides genuinely different recovery logic.
Best Practices
- Order catch blocks from specific exception types to general exception types.
- Give each catch block a meaningful recovery or reporting responsibility.
- Use multi-catch when unrelated exception types genuinely require identical handling.
- Avoid overly broad handlers when a more precise exception type is available.
- Keep exception handling readable; more catch blocks do not automatically mean better error handling.
Industry Insight: Good exception handling is not about catching the largest possible number of exceptions. It is about making failure behavior explicit. A developer reading your code should be able to understand which failures are expected, which are recoverable, and which should propagate further.
Interview Insights
A common interview question is: "Can we have multiple catch blocks for one try block?" Yes. Each catch block can handle a different exception type, and Java selects the first compatible handler.
Another common question is: "Why must the child exception be placed before the parent?" Because the parent handler is already capable of catching the child exception. Placing the parent first makes the child handler unreachable.
You may also be asked about multi-catch. The key point is that Java allows multiple unrelated exception types to share one catch block when the handling logic is identical, using the | operator.
Quick Revision
| Concept | Key Idea |
|---|---|
| Multiple catch | Several catch blocks can follow one try block. |
| Execution | Only the first compatible catch block executes. |
| Ordering | Specific exceptions must come before broader parent types. |
| Parent catch | Can handle compatible child exceptions as a broader fallback. |
| Multi-catch | Several unrelated exception types can share one handler. |
| Best practice | Use separate handlers for different recovery strategies and multi-catch for identical handling. |
Multiple catch blocks give Java programs a precise way to respond to different failure conditions without turning exception handling into a tangled mess. Once you understand how Java selects the first compatible handler and why specific exceptions must come before their parents, you can design much clearer error-handling logic. The next step is the finally block, which addresses an equally important question: what code should run whether an exception occurs or not?
