A Stream can have a close handler, but a terminal operation does not by itself invoke that handler.
Java Stream.close: terminal traversal does not close every source
The complete program targets Java 8. Compile it as one source file; its output is checked against the lesson.
Traversal and resource release are different events
A file-backed stream can hold a reader while elements are processed. Reaching count() or collecting the values is not a general resource-release protocol. Close the stream with try-with-resources when its source owns an external resource.
This fixture uses Stream.of and an onClose counter rather than opening a file, making the lifecycle distinction visible without depending on filesystem state. The count completes while the close counter remains zero; leaving the try block changes it to one.
Do not return ownership ambiguously
A helper that returns a file-backed stream transfers a resource obligation to its caller. Document that obligation or collect the required bounded data inside the helper and close it there. Collecting an unbounded input is not a safe way to avoid documenting ownership.
A stream is single-use. Closing one also prevents later traversal, and exceptions in processing still require release. If traversal and closing both fail, try-with-resources records the closing failure as suppressed rather than replacing the original failure.
Working program
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
public class ReceiptStreamLifetime {
public static void main(String[] args) {
AtomicInteger closed = new AtomicInteger();
try (Stream<String> receiptIds = Stream.of("R-21", "R-22").onClose(closed::incrementAndGet)) {
System.out.println(receiptIds.count());
System.out.println("before close=" + closed.get());
}
System.out.println("after close=" + closed.get());
}
}Output
2
before close=0
after close=1Costs and boundaries
The example uses constant application state and a two-element source. A real lazy source can bound memory while still keeping descriptors open; memory usage and descriptor lifetime require separate checks.
Common Mistakes
- Do not assume collect() closes a Files.lines stream.
- Do not return a resource-backed stream without explaining who closes it.
- Do not reuse a consumed stream.
Read next
Java file I/O: UTF-8, streaming reads, and path ownership, Java try-with-resources: close order and suppressed failures, Java streams: lazy pipelines and bounded results.
