Streams That Hold a Resource
Files.lines opens a real file handle. Close it, or you leak one on every call.
Open this lesson in the learning hubKey points
- Most streams own nothing, so you never close them. A few sit on an open file or socket and must be closed.
Files.lines,Files.listandFiles.walkkeep a handle open until the stream is closed.- Wrap them in try-with-resources:
try (Stream<String> s = Files.lines(p)) { ... }. String.lines()holds nothing extra, so it needs no close.BufferedReader.lines()leaves the reader open.onClose(runnable)registers a handler andclose()runs every one that was registered.
Example
import java.io.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) throws IOException {
String doc = "alpha\nbeta\ngamma";
// A String is already in memory, so this stream owns nothing. No close needed.
System.out.println("in memory : " + doc.lines().filter(l -> l.length() == 4).toList());
// A reader-backed stream sits on a resource. try-with-resources closes both.
try (BufferedReader reader = new BufferedReader(new StringReader(doc));
Stream<String> lines = reader.lines()) {
System.out.println("reader : " + lines.map(String::toUpperCase).toList());
}
System.out.println("reader : closed, even if the body had thrown");
// onClose registers handlers; close() runs every one of them.
try (Stream<String> s = Stream.of("a", "b")
.onClose(() -> System.out.println("onClose : handler 1"))
.onClose(() -> System.out.println("onClose : handler 2"))) {
System.out.println("count : " + s.count());
}
// Files.lines(path) is the real case, and the shape is exactly the same.
System.out.println("rule : if it touches the filesystem, close it");
}
}
If the stream came from the filesystem, it belongs in a try-with-resources.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Streams course, and every lesson in it is listed on the Streams contents page.