Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
56: Multi-Catch Blocks
I was working on a small utility recently that reads a "timeout" value from a config file and uses it to index into a predefined array of retry strategies. It's a simple task, but it's a minefield of potential exceptions. Let me show you the mess I started with.
try {
String line = Files.readString(Path.of("config.txt")).trim();
int timeout = Integer.parseInt(line);
Strategy strategy = retryStrategies[timeout];
System.out.println("Using strategy: " + strategy);
} catch (IOException e) {
System.err.println("Config file missing or unreadable. Using default.");
applyDefaultStrategy();
} catch (NumberFormatException e) {
System.err.println("Config value isn't a number. Using default.");
applyDefaultStrategy();
} catch (ArrayIndexOutOfBoundsException e) {
System.err.println("Timeout value is out of range. Using default.");
applyDefaultStrategy();
}
The Copy-Paste Fatigue
Looking at that, you probably notice the same thing I did: I'm repeating myself. Three different catch blocks, but they all essentially do the same thing—log a problem and fall back to a default strategy. It feels clunky. Whenever I see the same line of code appearing three times in a row, my internal "refactor alarm" goes off.
My first instinct might be to just catch the parent class. Since all of these are `Exceptions`, I could just do catch (Exception e). But that's a dangerous habit. If I catch Exception, I'm also catching NullPointerException or some other runtime disaster that I actually *want* the program to crash on so I can find the bug. Being too broad is how you hide critical failures in production.
Cleaning Up with the Pipe Operator
Java provides a much cleaner middle ground called the multi-catch block. Instead of writing separate blocks for every specific exception, we can group them using a single pipe symbol (|). Let's try rewriting my logic this way.
try {
String line = Files.readString(Path.of("config.txt")).trim();
int timeout = Integer.parseInt(line);
Strategy strategy = retryStrategies[timeout];
System.out.println("Using strategy: " + strategy);
} catch (IOException | NumberFormatException | ArrayIndexOutOfBoundsException e) {
System.err.println("Configuration error encountered. Falling back to default.");
applyDefaultStrategy();
}
Now the code is breathable. I'm still being explicit about which exceptions I'm willing to handle, but I've collapsed the redundant logic into one place. One thing to keep in mind here: the variable e in a multi-catch is implicitly final. You can't reassign it inside the block, though in most real-world scenarios, you wouldn't want to anyway.
A Small Catch on the Multi-Catch
While I was playing with this, I tried to add FileNotFoundException to that list, thinking I'd be even more specific. But the compiler stopped me immediately. It gave me an error saying that FileNotFoundException is already covered by IOException.
This is a key rule: you cannot list two exceptions in a multi-catch if one is a subclass of the other. Since FileNotFoundException extends IOException, listing both is redundant. Java forces you to pick one or the other to avoid ambiguity. If you want to handle a specific subclass differently, you have to give it its own catch block before the multi-catch block that handles the parent.
It's a small detail, but it's the kind of thing that will make you stare at your IDE for five minutes wondering why the code won't compile.
📋 Practical Task
Refactoring the Network Configuration Parser
You are reviewing a teammate's code for a network utility. They have written a method that parses a port number from a string and attempts to open a connection. The current implementation is repetitive and violates the DRY (Don't Repeat Yourself) principle.
Your Task: Refactor the following code to use a multi-catch block. Ensure that you only group exceptions that would logically result in the same recovery action (logging a "Connection Setup Failed" message and returning false).
public boolean setupConnection(String portStr) {
try {
int port = Integer.parseInt(portStr);
if (port < 0 || port > 65535) {
throw new IllegalArgumentException("Port out of range");
}
openSocket(port);
return true;
} catch (NumberFormatException e) {
System.out.println("Connection Setup Failed: Invalid number format.");
return false;
} catch (IllegalArgumentException e) {
System.out.println("Connection Setup Failed: Port value is illegal.");
return false;
} catch (IOException e) {
System.out.println("Connection Setup Failed: Network error.");
return false;
} catch (NullPointerException e) {
// This should NOT be caught in the multi-catch;
// let it propagate as a critical bug.
throw e;
}
}
Modify the code so that NumberFormatException, IllegalArgumentException, and IOException are handled by a single catch block, while keeping the NullPointerException behavior unchanged.
There are no comments for now.