Java try-with-resources — Automatic Resource Management
try-with-resources (Java 7+) automatically closes resources when the block exits — no finally needed. It’s cleaner, safer, and eliminates an entire class of resource-leak bugs.
The Problem It Solves
Traditional resource management is verbose and error-prone:
// ❌ OLD WAY — manual cleanup
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(); // close itself can throw!
} catch (IOException e) {
e.printStackTrace();
}
}
}
With try-with-resources:
// ✅ MODERN WAY — automatic cleanup
try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
String line = reader.readLine();
System.out.println(line);
} catch (IOException e) {
e.printStackTrace();
}
// reader.close() called automatically
Syntax
try (ResourceType resource = new ResourceType()) {
// use resource
} catch (ExceptionType e) {
// handle
}
// resource is automatically closed here
The resource must implement AutoCloseable (or its parent Closeable).
The AutoCloseable Interface
public interface AutoCloseable {
void close() throws Exception;
}
Any class implementing AutoCloseable can be used with try-with-resources:
| Class | What It Closes |
|---|---|
FileReader / FileWriter |
File handles |
BufferedReader / BufferedWriter |
Buffered streams |
InputStream / OutputStream |
Byte streams |
Connection |
Database connections |
PreparedStatement |
SQL statements |
ResultSet |
Query results |
Socket |
Network connections |
Scanner |
Input scanner |
Multiple Resources
Declare multiple resources separated by semicolons — closed in reverse order:
try (
FileInputStream fis = new FileInputStream("input.txt");
FileOutputStream fos = new FileOutputStream("output.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos)
) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
}
// Closed in reverse: bos → bis → fos → fis
Why Reverse Order?
Outer resources often depend on inner ones. Closing inner first would break the outer:
Open: fis → fos → bis → bos
Close: bos → bis → fos → fis (reverse — safe)
Java 9 Enhancement — Effectively Final Variables
Java 9 lets you use pre-declared resources if they’re effectively final:
// Java 7-8: must declare in try()
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
// ❌ Java 7-8: compile error
try (reader) { ... }
// ✅ Java 9+: works if reader is effectively final
try (reader) {
System.out.println(reader.readLine());
}
Useful when resources are passed as parameters:
public void process(Connection conn) throws SQLException {
try (conn) { // Java 9+ — conn is effectively final
// use connection
}
// conn is closed automatically
}
Suppressed Exceptions
What happens when both the try block AND close() throw exceptions?
public class FaultyResource implements AutoCloseable {
public void doWork() {
throw new RuntimeException("from doWork");
}
@Override
public void close() {
throw new RuntimeException("from close");
}
}
try (FaultyResource r = new FaultyResource()) {
r.doWork();
}
Result:
- The
tryblock exception (from doWork) is the primary exception. - The
close()exception (from close) is a suppressed exception.
catch (RuntimeException e) {
System.out.println(e.getMessage()); // "from doWork"
Throwable[] suppressed = e.getSuppressed();
System.out.println(suppressed[0].getMessage()); // "from close"
}
With old-style
finally, theclose()exception would replace the original — losing critical information. try-with-resources preserves both.
Creating Custom AutoCloseable Classes
public class DatabaseConnection implements AutoCloseable {
private final String url;
private boolean open;
public DatabaseConnection(String url) {
this.url = url;
this.open = true;
System.out.println("Connected to " + url);
}
public void query(String sql) {
if (!open) throw new IllegalStateException("Connection closed");
System.out.println("Executing: " + sql);
}
@Override
public void close() {
if (open) {
open = false;
System.out.println("Disconnected from " + url);
}
}
}
Usage:
try (var db = new DatabaseConnection("jdbc:mysql://localhost/mydb")) {
db.query("SELECT * FROM users");
db.query("SELECT * FROM orders");
}
// Output:
// Connected to jdbc:mysql://localhost/mydb
// Executing: SELECT * FROM users
// Executing: SELECT * FROM orders
// Disconnected from jdbc:mysql://localhost/mydb
Closeable vs AutoCloseable
| Feature | AutoCloseable |
Closeable |
|---|---|---|
| Package | java.lang |
java.io |
close() throws |
Exception |
IOException |
| Idempotent requirement | No | Yes (should be safe to call multiple times) |
| Used by | try-with-resources | I/O classes |
Closeable extends AutoCloseable — all Closeable types work with try-with-resources.
Practical Patterns
Database Query
try (
Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?")
) {
stmt.setLong(1, userId);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return new User(rs.getString("name"), rs.getString("email"));
}
}
}
Reading a File Line by Line
try (var reader = new BufferedReader(new FileReader("data.csv"))) {
String line;
while ((line = reader.readLine()) != null) {
processLine(line);
}
}
Modern File Reading (NIO)
try (Stream<String> lines = Files.lines(Path.of("data.txt"))) {
lines.filter(line -> !line.isBlank())
.map(String::trim)
.forEach(System.out::println);
}
Without catch — Just Cleanup
try-with-resources doesn’t require a catch block:
try (var conn = getConnection()) {
execute(conn);
}
// conn closed automatically — exception propagates to caller
Common Mistakes
| Mistake | Problem | Fix |
|---|---|---|
| Not using try-with-resources | Resource leaks | Use it for all AutoCloseable |
Closing manually in finally too |
Double close | Let try-with-resources handle it |
| Ignoring suppressed exceptions | Lost error info | Check getSuppressed() |
Resource not AutoCloseable |
Can’t use in try-with-resources | Implement the interface |
| Assigning to resource var in try body | Breaks auto-close | Declare resource in try() header |
Key Takeaways
- Always use try-with-resources for
AutoCloseableobjects — files, streams, connections. - Resources are closed in reverse declaration order.
- Suppressed exceptions from
close()are preserved, not lost. - Java 9+ allows effectively final variables in the
try()header. - Implement
AutoCloseablein your own classes that hold resources. - try-with-resources makes
finallyblocks for cleanup obsolete.
What’s Next?
Next: Exception Handling Best Practices — patterns, anti-patterns, and production-grade error handling.