关闭一个流会同时关闭BufferedReader的来源吗?

13

文档中了解到:

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

当我使用以下方式在BufferedReader上使用lines()方法创建Stream<String>时,关闭Stream是否也会关闭BufferedReader

try (Stream<String> lines = new BufferedReader(new InputStreamReader(process.getInputStream())).lines()) {
  // Do stuff
}

// Is the BufferedReader, InputStreamReader and InputStream closed?

我尝试了一些快速测试,结果显示不需要关闭BufferedReaderBufferedReaderin字段不为null),但是接下来的这句话让我感到困惑,因为这个例子中也涉及I/O,对吧?

通常只有源自IO通道的流(例如由Files.lines(Path, Charset)返回的流)才需要关闭。

如果不是这种情况,我需要关闭两个实例还是只需关闭BufferedReader


理想情况下,我希望从某个方法返回一个Stream<String>,而无需客户端担心读取器。目前,我创建了一个Stream装饰器,它还会关闭读取器,但如果不必要,那将更容易些。


你在哪里测试 BufferedReader 是否关闭?在 try 块内部吗? - user1907906
我已经在明确调用close关闭流后进行了测试,并使用调试器检查了字段。自动关闭的尝试是为了说明我想要如何使用它。 - nhaarman
参见:https://dev59.com/zlsX5IYBdhLWcg3wPdU7#34073306 - Brian Goetz
3个回答

10

如果您想推迟关闭读取器以交付流,则需要调用Stream.onClose()

static Stream<String> toStream(BufferedReader br){
    return br.lines().onClose(asUncheckedAutoCloseable(br));
}


static Runnable asUncheckedAutoCloseable(AutoCloseable ac) {
    return () -> {
        try {
            ac.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    };
}

我稍微编辑了一下你的回答...希望你不介意——我给你一个应得的加1。 - Eugene

3
不,似乎不行。因为该流是使用创建的。
    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
            iter, Spliterator.ORDERED | Spliterator.NONNULL), false);

其中不传递对BufferedReader的任何引用


1
问题中没有调用 public static Stream<String> lines(Path path, Charset cs) 方法。调用的是 Reader.lines() 方法。 - user1907906
这似乎是一个有说服力的论点,说明为什么BufferedReader不应该从Stream中关闭。查看lines的源代码,我没有发现必须关闭Stream的原因,因此我得出结论,只有BufferedReader应该被关闭。 - nhaarman
关于你问题的第二部分 - 请尝试使用 Files.lines(Path, Charset) - JiriS
1
很遗憾,我不是从文件中读取,而是从进程流中读取。 - nhaarman

2

在你的问题中,你没有展示如何创建Reader,它是new BufferedReader(in)的参数。但是从我的测试中,没有理由认为Stream会关闭这个参数。

执行以下操作应该可以关闭所有内容:

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.stream.Stream;

public class SOPlayground {

    public static void main(String[] args) throws Exception {
        try (Reader in = new InputStreamReader(new FileInputStream(new File("/tmp/foo.html")));
                BufferedReader reader = new BufferedReader(in);
                Stream<String> lines = reader.lines()) {
            lines.forEach(System.out::println);
        }        
    }
}

谢谢,我也已经找到了。我已经在问题中添加了一些额外的信息。 - nhaarman
1
使用 try(Stream<String> lines = Files.lines(Paths.get("/tmp/foo.html"))) {...} 会更简单。 - Tagir Valeev
请记住,Tagir,OP的用例不是从文件中读取,而是从进程的“InputStream”中读取。 - Ti Strga

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接