8.4 try-with-resources and Advanced Exception Handling
Java 7's try-with-resources solves the hassle and mistakes that arise when you must always close resources after using them.
1. The Problem with Resource Management
Files, database connections, and network sockets must be closed after use. The old way required a finally block and was error-prone.
// ❌ Old style - verbose and error-prone
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("data.txt"));
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try { reader.close(); } catch (IOException e) { e.printStackTrace(); }
}
}
2. try-with-resources (Java 7+)
Declare resources in try ( ... ) parentheses and they are automatically closed when the block exits — whether normally or via exception.
Requirement: The resource class must implement
java.lang.AutoCloseable(orjava.io.Closeable).
// ✅ try-with-resources - clean and safe!
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
System.out.println("File read error: " + e.getMessage());
}
// reader.close() is called automatically!
Managing Multiple Resources
try (
FileInputStream in = new FileInputStream("source.txt");
FileOutputStream out = new FileOutputStream("dest.txt")
) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
System.out.println("File copy complete!");
} catch (IOException e) {
e.printStackTrace();
}
Custom AutoCloseable
class DatabaseConnection implements AutoCloseable {
DatabaseConnection(String url) { System.out.println("DB connected: " + url); }
void query(String sql) { System.out.println("Query: " + sql); }
@Override
public void close() { System.out.println("DB connection closed"); }
}
try (DatabaseConnection conn = new DatabaseConnection("jdbc:mysql://localhost/shop")) {
conn.query("SELECT * FROM users");
} // conn.close() called automatically
3. Multi-catch (Java 7+)
Combine multiple exceptions in one catch block using the | operator.
// ❌ Old: duplicate catch blocks
try { /* ... */ }
catch (FileNotFoundException e) { handle(e); }
catch (ParseException e) { handle(e); }
// ✅ Multi-catch
try {
String data = readFile("config.txt");
int value = Integer.parseInt(data);
} catch (IOException | NumberFormatException e) {
System.out.println("Data processing error: " + e.getMessage());
}
4. Exception Chaining
Preserve the original cause when wrapping an exception.
class DataService {
void loadData(String path) throws DataLoadException {
try {
new FileReader(path);
} catch (FileNotFoundException e) {
throw new DataLoadException("Data file not found: " + path, e);
}
}
}
class DataLoadException extends Exception {
DataLoadException(String message, Throwable cause) { super(message, cause); }
}
try {
new DataService().loadData("missing.csv");
} catch (DataLoadException e) {
System.out.println("High-level error: " + e.getMessage());
System.out.println("Cause: " + e.getCause().getMessage());
}
5. Checked vs Unchecked Exceptions
| Type | Examples | Handling |
|---|---|---|
| Checked | IOException, SQLException | Must use try-catch or declare throws |
| Unchecked | NullPointerException, IllegalArgumentException | Optional |
| Error | OutOfMemoryError, StackOverflowError | Generally not handled |
Real-world exception handling patterns:
-
Checked → Unchecked wrapping: Wrap library-checked exceptions at API boundaries to avoid forcing callers to write try-catch.
static String readFileSafely(String path) {
try { return Files.readString(Path.of(path)); }
catch (IOException e) { throw new RuntimeException("File read failed: " + path, e); }
} -
Context in error messages: "File not found" is less useful than "Profile image for user ID 123 (/uploads/123.jpg) not found."
-
Lambda-based resource helper pattern:
static <T> T withDb(String url, ThrowingSupplier<T> action) {
try (var conn = new DatabaseConnection(url)) { return action.get(); }
catch (Exception e) { throw new RuntimeException(e); }
}