java-为什么Files.lines(和类似的Streams)没有自动关闭?

Stream的Javadoc状态:

流具有BaseStream.close()方法并实现AutoCloseable,但是实际上几乎所有流实例在使用后实际上都不需要关闭。 通常,只有源是IO通道的流(例如,由Files.lines(Path,Charset)返回的流)才需要关闭。 大多数流都由集合,数组或生成函数支持,而无需特殊的资源管理。 (如果流确实需要关闭,则可以在try-with-resources语句中将其声明为资源。)

因此,绝大多数情况下,人们可以像Files.lines那样使用单行流,但是对于Files.lines和其他资源支持的流,则必须使用try-with-resources语句,否则会泄漏资源。

这使我感到容易出错且不必要。 由于Streams只能被迭代一次,因此在我看来,没有这样的情况,即Files.lines的输出不应在被迭代后立即关闭,因此实现应仅在任何终端的末尾隐式调用close。 操作。 我错了吗?

MikeFHay asked 2020-02-14T11:35:31Z
4个解决方案
61 votes

是的,这是一个蓄意的决定。 我们考虑了两种选择。

这里的操作设计原则是“谁获取资源都应该释放资源”。 当您读到EOF时,文件不会自动关闭; 我们希望文件被打开者明确关闭。 IO资源支持的流是相同的。

幸运的是,该语言为您提供了一种自动化的机制:try-with-resources。 由于Stream实现了AutoCloseable,因此您可以执行以下操作:

try (Stream<String> s = Files.lines(...)) {
    s.forEach(...);
}

“自动关闭真的很方便,所以我可以将它写成单线”这样的说法很好,但主要是尾巴摇动了狗。 如果您打开了文件或其他资源,则还应该准备关闭它。 有效和一致的资源管理胜过“我想用一行编写的内容”,而我们选择不失真设计只是为了保持一行式。

Brian Goetz answered 2020-02-14T11:36:00Z
15 votes

除了@BrianGoetz答案之外,我还有其他更具体的示例。 不要忘记hasNext()具有转义填充方法,例如next()。假设您正在这样做:

Iterator<String> iterator = Files.lines(path).iterator();

之后,您可以多次调用hasNext()next(),然后放弃该迭代器:Iterator接口完全支持这种用法。 无法显式关闭Iterator,您只能在此处关闭的对象是Stream。因此,这种方法可以正常工作:

try(Stream<String> stream = Files.lines(path)) {
    Iterator<String> iterator = stream.iterator();
    // use iterator in any way you want and abandon it at any moment
} // file is correctly closed here.
Tagir Valeev answered 2020-02-14T11:36:25Z
4 votes

另外,如果要“一行写”。 您可以这样做:

Files.readAllLines(source).stream().forEach(...);

如果确定需要整个文件并且文件很小,则可以使用它。 因为这不是一个懒惰的阅读。

Michael Starodynov answered 2020-02-14T11:36:50Z
1 votes

如果您像我一样懒惰,并且不介意“如果引发异常,它将使文件句柄保持打开状态”,则可以将流包装在自动关闭的流中,如下所示(可能还有其他方式):

  static Stream<String> allLinesCloseAtEnd(String filename) throws IOException {
    Stream<String> lines = Files.lines(Paths.get(filename));
    Iterator<String> linesIter = lines.iterator();

    Iterator it = new Iterator() {
      @Override
      public boolean hasNext() {
        if (!linesIter.hasNext()) {
          lines.close(); // auto-close when reach end
          return false;
        }
        return true;
      }

      @Override
      public Object next() {
        return linesIter.next();
      }
    };
    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT), false);
  }
rogerdpack answered 2020-02-14T11:37:11Z
translate from https://stackoverflow.com:/questions/34072035/why-is-files-lines-and-similar-streams-not-automatically-closed