使用try-with-resources语句声明Stream之间的区别是什么?

时间:2017-11-04 06:33:37

标签: java java-8 java-stream

在Java 8中,Stream(AutoCloseable)无法重复使用,一旦使用它,流将被关闭。那么使用try-with-resources语句声明的实用程序是什么?

使用try-with-resources语句的示例:

    public static void main(String[] args) throws IOException {

    try (Stream<Path> entries
            = Files.walk(Paths.get("."), 4, FileVisitOption.FOLLOW_LINKS)) {
        entries.forEach(x -> System.out.println(x.toAbsolutePath()));// the entries stream will be automatically closed at this point
        //..
        System.out.println("Still in the Try Block");
    } //The entries will be closed again because it is declared in the try-with-resources statement
}

这里没有try catch块的相同示例

public static void main(String[] args) throws IOException {
        Stream<Path> entries = Files.walk(Paths.get("."), 4, FileVisitOption.FOLLOW_LINKS);
        entries.forEach(x -> System.out.println(x.toAbsolutePath()));// the entries stream will be automatically closed at this point
        System.out.println("Is there a risk of resources leak ?");
    }

哪一个更安全?

在一些答案之后,我更新了我的代码以检查流是否已经关闭:

这里是新代码:

public static void main(String[] args) throws IOException {

    resourceWithTry();
    resourceWithoutTry();
}

private static void resourceWithTry() throws IOException {
    try (Stream<Path> entries
            = Files.walk(Paths.get("."), 4, FileVisitOption.FOLLOW_LINKS).onClose(() -> System.out.println("The Stream is closed"))) {
        entries.forEach(x -> System.out.println(x.toAbsolutePath()));// the entries stream will be not automatically closed at this point
        System.out.println("Still in the Try Block");
    } //The entries will be closed again because it is declared in the try-with-resources statement
}

private static void resourceWithoutTry() throws IOException {
    Stream<Path> entries
            = Files.walk(Paths.get("."), 4, FileVisitOption.FOLLOW_LINKS).onClose(() -> System.out.println("Without Try: The Stream is closed"));
    entries.forEach(x -> System.out.println(x.toAbsolutePath()));// the entries stream will be not automatically closed at this point
    System.out.println("Still in the Try Block");
}

3 个答案:

答案 0 :(得分:8)

  

在Java 8中,Stream(AutoCloseable)一旦被重用就无法重用   消耗或使用,流将被关闭。

不完全。
StreamforEach()终端操作不会关闭流 它使流管道不再消耗。
哪个不同。

Package java.util.stream description州:

  

执行终端操作后,流管道为   被认为已消耗,不能再使用;如果你需要   再次遍历相同的数据源,您必须返回数据   获取新流的来源。

没有说流已关闭。

因此,在以下代码中,永远不会调用AutoCloseable.close()实例的Stream方法:

Stream<Path> entries = Files.walk(Paths.get("."), 4, FileVisitOption.FOLLOW_LINKS);
entries.forEach(x -> System.out.println(x.toAbsolutePath()));

所以总是需要关闭流吗? (使用明确的close()调用或更好的try-with-resources

java.util.stream.Stream<T> javadocs解释说,使用后几乎所有Stream个实例都不需要使用AutoCloseable.close()关闭。
这只适用于IO流。

  

Streams有一个close()方法并实现AutoCloseable,但差不多   所有流实例实际上不需要在使用后关闭。   通常,只有源是IO通道的流(例如那些   由Files.lines(Path,Charset)返回)将需要关闭。最   流由集合,数组或生成函数支持,   这不需要特殊的资源管理。 (如果有流   要求关闭,可以将其声明为a中的资源   尝试使用资源声明。)

在我们的例子中,你操纵File,所以关闭频道是有意义的。
通过使用try-with-resources,可以避免可能发生的资源耗尽异常和错误 如果在处理过程中出现任何错误或异常,则资源可能会全部释放。

答案 1 :(得分:3)

流处理可能会中断,然后它不会被完全消耗,也不会自动关闭。 考虑这个例子:

Stream<Integer> nums = Stream.of(1, 2, 3);
nums.onClose(() -> System.out.println("close was called"));
nums.forEach(x -> { if (x > 1) throw new IllegalStateException(); });

这将在中间崩溃,onClose将不会被调用。 流保持打开状态,您必须手动关闭它。

如果您使用 try-with-resources ,它将被正确关闭,无论如何, 你可以观察到:

try (Stream<Integer> nums = Stream.of(1, 2, 3)) {
    nums.onClose(() -> System.out.println("close was called"));
    nums.forEach(x -> { if (x > 1) throw new IllegalStateException(); });
}

这是一个折磨的例子, 因为它不消耗任何需要关闭的资源, 垃圾收集者将清理, 即使溪流没有关闭。

  

那么使用try-with-resources语句声明的实用程序是什么?

确保流无论如何都会被关闭。 当流不受需要关闭的资源支持时, 它实际上并不重要。

上面的例子只是为了证明这种差异, 但在这样的代码中,使用try-with-resources是不必要的偏执狂, 只是代码中的噪音,所以没有try-with-resources就更好了。

答案 2 :(得分:1)

根据docs,建议(并且更安全)使用try-with-resources关闭此实例中返回的流:

  

如果需要及时处理文件系统资源,则   应该使用try-with-resources构造来确保   在流操作之后调用stream的close方法   完成。

在其他情况下,Stream从数组或列表中提供,它没有任何区别。