try-with-resources
try-with-resources guarantees that anything implementing AutoCloseable is closed when the block exits — by normal completion or by exception. It replaces the verbose try/finally pattern and removes the entire class of "I forgot to close the stream" bugs.
try-with-resources patterns and gotchas
EXAMPLE
import java.io.*;
import java.nio.file.*;
import java.sql.*;
import java.util.zip.*;
public class TryWithResources {
// 1) Single resource
public static String read(Path p) throws IOException {
try (var reader = Files.newBufferedReader(p)) {
return reader.readLine();
} // reader.close() is called automatically
}
// 2) Multiple resources — semicolon-separated, closed in REVERSE order
public static void copy(Path src, Path dst) throws IOException {
try (var in = Files.newInputStream(src);
var out = Files.newOutputStream(dst)) {
in.transferTo(out);
} // out.close() THEN in.close()
}
// 3) Database — Connection, Statement, ResultSet all AutoCloseable
public static int countOrders(String url, String user, String pw) throws SQLException {
try (var conn = DriverManager.getConnection(url, user, pw);
var stmt = conn.prepareStatement("SELECT COUNT(*) FROM orders");
var rs = stmt.executeQuery()) {
return rs.next() ? rs.getInt(1) : 0;
}
}
// 4) Streams (Stream<T>) implement AutoCloseable — close to release file handles
public static long countLines(Path p) throws IOException {
try (var stream = Files.lines(p)) {
return stream.count();
}
}
// 5) Suppressed exceptions — if both the body AND close() throw,
// the BODY exception is primary; the close exception is added as suppressed.
public static void demo() throws IOException {
try (var noisy = new NoisyClose()) {
throw new IOException("primary");
}
// catch -> ex.getSuppressed() returns [NoisyClose.close exception]
}
static class NoisyClose implements AutoCloseable {
@Override public void close() throws IOException {
throw new IOException("close failed");
}
}
// 6) Reusing an EXISTING variable (Java 9+) — no re-declaration
public static void reuse(BufferedReader existing) throws IOException {
try (existing) {
existing.readLine();
}
}
// 7) Wrapping streams stays simple
public static void writeGzipped(Path p, String text) throws IOException {
try (var out = new GZIPOutputStream(Files.newOutputStream(p));
var w = new OutputStreamWriter(out)) {
w.write(text);
}
}
// 8) Common pitfalls
// - Trying to use a resource AFTER the try block (it has been closed)
// - Implementing AutoCloseable without making close() idempotent
// - Catching exception INSIDE the try and continuing to use the resource
// that may already be in a bad state
// - Forgetting that Streams from Files.lines / Files.list need closing
// 9) Custom AutoCloseable
public static class Tx implements AutoCloseable {
private final Connection conn;
private boolean committed;
public Tx(Connection conn) throws SQLException { this.conn = conn; conn.setAutoCommit(false); }
public void commit() throws SQLException { conn.commit(); committed = true; }
@Override public void close() throws SQLException {
if (!committed) conn.rollback();
conn.setAutoCommit(true);
}
}
// Usage:
// try (var tx = new Tx(conn)) {
// doWork(conn);
// tx.commit();
// } // rollback() runs if commit() was not called
}
Why it matters
Move every IO / DB call to try-with-resources from the moment you start a Java codebase. The pattern is shorter than try/finally, never leaks handles on exceptions, and the suppressed-exception story makes "primary failure + cleanup failure" the readable single stack trace it should be.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
try (var in = new BufferedReader(new FileReader("input.txt"))) {
return in.lines().count();
}
Try it Yourself »
Discussion
Loading…