为什么 Java close() 在发出终端操作后不流式传输?



阅读后https://www.airpair.com/java/posts/spring-streams-memory-efficiency,我很想从数据库中流式传输结果,但正如我与一位同事讨论的那样(他在文章中添加了cfr.comment),需要记住使用try-with-resources结构来避免任何内存泄漏。

  1. 为什么Java 8库不在每次终端操作后自行关闭流(而不必在尝试使用资源时包装流实例化)
  2. 如果适用,是否有任何计划将此功能添加到Java中,或者请求它是否有意义

因为需要显式资源释放的流实际上是一种非常不寻常的情况。因此,我们选择不使用仅对.01%的使用有价值的东西来加重所有流执行的负担。

我们使流可自动关闭,以便可以根据需要从源代码中释放资源,但这是我们停止的地方,也是有充分理由的。

这样做不仅会自动给大多数用户带来他们不需要的额外工作,而且还会违反一个一般原则:分配资源的人负责关闭资源。当你呼叫时

BufferedReader reader = ...
reader.lines().op().op()...

是打开资源的人,而不是流库的人,应该关闭它。事实上,由于关闭某个资源持有对象上调用访问器方法所产生的流有时会关闭底层对象,您可能不希望流为您关闭BufferedReader,您可能希望它在调用后保持打开状态。

如果你想关闭资源,这也很容易:

try (BufferedReader reader = ...) {
    reader.lines().op()...
}

您可能以一种特定的方式使用流,所以流应该做什么似乎很"明显"——但有比您更多的用例。因此,我们没有迎合特定的用例,而是从一般原则来处理它:如果你打开了流,并且你想关闭它,你自己关闭它,但如果你没有打开它,你就不能关闭它。

我认为您将java.io.InputStreamjava.util.stream.Stream混合在一起,这是两个非常不同的概念。

try-with-resources处理实现Autoclosable接口的对象,例如InputStream s。InputStream s表示与IO相关的抽象数据源。

另一方面,java.util.stream.Stream<T>实现了函数编程的一个概念,它表示一种动态集合,这种集合不一定是静态构建的,而是可以生成的,因此可能是无限的。

Marko Topolnik(您链接的文章的作者)在文章中主要做的是提出一种将IO源代码封装为java.util.stream.Stream的方法。这是一个非常聪明的方法,但java.util.stream.Stream通常不用于此目的。

由于它们通常不用于IO,因此没有理由在终端操作后包含关闭


编辑

在你澄清了你实际上没有混淆这两者之后(很抱歉这么认为),多亏了这个答案,我发现你的确切例子在AutoCloseable的文档中得到了回答(我自己加了重点):

基类实现是可能的,事实上也是常见的AutoCloseable,即使不是它的所有子类或实例都会持有可释放的资源。对于必须完整运行的代码一般性,或者当已知AutoCloseable实例需要释放资源,建议使用try with resources建筑但是,当使用Stream等设施时同时支持基于I/O和非基于I/O的表单,尝试使用资源当使用非基于I/O的形式时,块通常是不必要的

为什么Java 8库不在每次终端操作后自行关闭流(而不必在尝试使用资源时包装流实例化)?

因为异常可能发生在终端操作期间或之前,并且您可能不希望终端操作关闭流。如果您确实希望关闭流,则可以使用try with resource。

如果适用,是否有任何计划将此功能添加到Java中,或者请求它是否有意义?

这是没有道理的,见上面的答案。

最新更新