Closing resources in Java
try-with-resources for files and streams; Files.lines must be closed.
Borrowed handles
Files, sockets and streams hold operating-system handles, and the OS gives each process only a limited number. try-with-resources closes anything that implements **AutoCloseable on every** way out of the block: normal end, return or exception.
try (var in = Files.newBufferedReader(p)) {
return in.readLine();
} // in.close() runs here, alwaysClose before catch?
The resource prints when it closes. What prints?
try (AutoCloseable a = () ->
System.out.println("closed")) {
throw new IllegalStateException();
} catch (Exception e) {
System.out.println("caught");
}caught closedclosed caughtcaught
Show the answer
closed then caught — the resource is closed as soon as the try block exits, even by an exception, before the catch block runs.
Reverse order
With several resources, they close in reverse order of declaration: the later one may depend on the earlier one, so it goes first.
try (var in = Files.newBufferedReader(src);
var out = Files.newBufferedWriter(dst)) {
in.transferTo(out);
} // out closed, then inFiles.lines is a hidden open file
Files.lines (and Files.walk) keep the file open until the stream is closed — and a terminal op like count() does not close it. Each call leaks a handle. Put the stream in try-with-resources.
// leaks a file handle:
long n = Files.lines(Path.of("app.log"))
.filter(l -> l.contains("ERROR"))
.count();Must be AutoCloseable
Having a close() method isn't enough: a try-with-resources resource must **implement AutoCloseable** (or its subtype Closeable), otherwise it's a compile error.
class Conn { // no interface
void close() { }
}
try (var c = new Conn()) { } // won't compileTwo exceptions at once
The body throws exception X, then close() throws exception Y. What does your catch block receive?
Think about it, then reveal the answer
X, with Y attached as a suppressed exception (read it with X.getSuppressed()). The body's error is the important one. Old finally-based code lost X and reported Y instead.
Leaks in production
"Too many open files" outages usually trace back to one unclosed Files.lines, socket or JDBC connection in a hot path. Since Java 7, try-with-resources is the standard answer, and static analysers flag resources created outside it.
Key takeaways
- try (var in = …) { } closes in automatically
- Several resources close in reverse order of declaration
- Files.lines/Files.walk hold an open file — close them!
- close() failures become suppressed exceptions
try-with-resources arrived in Java 7, and Java 9 made it shorter: you can write try (existingVar) { … } with an effectively final variable declared earlier.
Practice questions
What does this print?
record R(String n) implements AutoCloseable {
public void close() {
System.out.println("close " + n);
}
}
void main() {
try (var a = new R("a"); var b = new R("b")) {
System.out.println("body");
}
}- body close a close b
- body close b close a
- close a close b body
- body
Check your answer
body close b close a. The body runs first, then resources are closed in reverse order: b before a.
What does this print?
class Conn {
void close() { System.out.println("x"); }
}
void main() {
try (var c = new Conn()) {
System.out.println("hi");
}
}- hi x
- hi
- Compile error
- Throws IllegalStateException
Check your answer
Compile error. Having a close() method isn't enough: a try-with-resources resource must implement AutoCloseable (or Closeable).