Files.lines()必须用try-with-resources显式关闭,否则文件句柄泄露易致“Too many open files”;正确写法是将Stream声明在try括号内,确保异常或正常退出时自动调用close()释放资源。

<p>使用 <code>Files.lines()</code> 时必须配合 try-with-resources,否则底层的 <code>Stream</code> 不会自动关闭,文件句柄将长期占用,尤其在读取大文件时容易触发“Too many open files”异常。</p>
<H3>为什么 Files.lines() 需要显式关闭</H3>
<p><code>Files.lines(Path)</code> 返回的是一个 <code>Stream<String></code>,但它背后封装了 <code>BufferedReader</code> 和底层文件通道。这个流是“可关闭的(closeable)”,但 Java 的 <code>Stream</code> 默认不会自动关闭——除非你用 try-with-resources 显式声明它为资源。</p>
<p>若仅写:<br>
<font color="red"><code>Stream<String> lines = Files.lines(path);</code></font><br>
即使后续调用了 <code>lines.forEach(...)</code> 或 <code>lines.count()</code>,流也不会自动关闭,句柄泄露风险极高。</p>
<H3>正确写法:try-with-resources 包裹 Stream</H3>
<p>把 <code>Stream</code> 声明在 try 括号内,JVM 会在 try 块退出(无论正常还是异常)时自动调用其 <code>close()</code> 方法:</p>
<ul>
<li>✅ 正确示例:</li>
</ul>
<p><code>try (Stream<String> lines = Files.lines(Paths.get("large.log"))) {<br>
lines.filter(line -> line.contains("ERROR"))<br>
.limit(10)<br>
.forEach(System.out::println);<br>
} catch (IOException e) {<br>
e.printStackTrace();<br>
}</code></p>
<p>此时即使中间抛出异常或提前 return,<code>lines.close()</code> 仍会被调用,释放文件句柄和缓冲区。</p>
<H3>常见错误与注意事项</H3>
<ul>
<li>❌ 不要把 stream 赋值给变量再在 try 外使用(破坏资源生命周期);</li>
<li>❌ 不要用 <code>stream.iterator()</code> 或 <code>stream.spliterator()</code> 后丢弃 stream 引用——关闭责任仍在 stream 上;</li>
<li>⚠️ 若需多次遍历,不能复用同一 stream(已关闭或已消费),应重新调用 <code>Files.lines()</code>;</li>
<li>✅ 支持指定字符集:<code>Files.lines(path, StandardCharsets.UTF_8)</code>,避免平台默认编码问题。</li>
</ul>
<H3>替代方案:何时考虑 BufferedReader</H3>
<p>如果逻辑复杂(如需要标记、回退、逐行状态维护),或者想更明确控制关闭时机,可直接用 <code>BufferedReader</code> + try-with-resources:</p>
<p><code>try (BufferedReader reader = Files.newBufferedReader(Paths.get("data.txt"))) {<br>
String line;<br>
while ((line = reader.readLine()) != null) {<br>
// 处理 line<br>
}<br>
}</code></p>
<p>这种方式语义更清晰,且无需担心 Stream 中间操作的关闭陷阱,适合对资源控制要求极高的场景。</p>