为什么 Java 在发出终端操作后不关闭() 流?

2022-09-01 16:00:38

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

  1. 为什么 Java 8 库不负责在每次终端操作后关闭流本身(而不必将流实例化包装在 try-with-resources 中)?
  2. 如果适用,是否有任何计划将此功能添加到 Java 中,或者请求它是否有意义?

答案 1

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

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

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

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

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

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

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

您可能以特定的方式使用流,因此流应该做什么似乎“显而易见” - 但是那里的用例比您的用例更多。因此,我们没有迎合特定的用例,而是从一般原则出发:如果您打开了流,并且希望它关闭,请自己关闭它,但是如果您没有打开它,则不适合您关闭它。


答案 2

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

try-with-resources适用于实现接口的对象,例如 s. s 表示与 相关的抽象数据源。AutoclosableInputStreamInputStreamIO

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

Marko Topolnik(您链接到的文章的作者)在文章中基本上所做的是建议一种将源代码包装成.这是一个非常聪明的方法,但通常不是为此目的而用的。IOjava.util.stream.Streamjava.util.stream.Stream

因为它们通常不打算与 一起使用,所以没有理由让它们在终端操作后包括关闭IO


编辑

在你澄清了你实际上并没有混淆这两者之后(很抱歉假设如此),多亏了这个答案,我发现你的确切例子在AutoCloseable的文档中得到了回答(着重号是我自己添加的):

基类实现 AutoCloseable 是可能的,事实上也是很常见的,即使并非所有子类或实例都包含可释放的资源。对于必须完全通用地运行的代码,或者当已知 AutoCloseable 实例需要资源释放时,建议使用 try-with-resources 构造。但是,当使用同时支持基于 I/O 和非基于 I/O 的表单(如 Stream)时,在使用非基于 I/O 的表单时,通常不需要尝试使用资源块。


推荐