2017-11-04 240 views
3

在Java 8中,Stream(它是AutoCloseable)不能重用,一旦它被使用或使用,流将被关闭。那么用try-with-resources声明来声明的效用是什么?声明Stream与try-with-resources声明之间有什么区别?

示例使用try-与资源声明:

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"); 
} 

回答

8

在Java 8,流(这是AutoCloseable)不能再被使用,一旦它 被消耗或使用的,流将被关闭。

不完全是。
Stream终端操作如forEach()不要关闭流。
它使流管线不再消耗。
这是不同的。

Package java.util.stream description状态:

执行终端操作之后,流管道 视为消耗,并且不能再使用;如果您需要 再次遍历相同的数据源,则必须返回数据源 以获取新的流。

它没有说流被关闭。

所以在下面的代码中,Stream实例的AutoCloseable.close()方法不会调用:

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>的javadoc解释说,几乎所有的Stream情况下不需要使用后要与AutoCloseable.close()关闭。
这只适用于IO流。

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

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

+0

我更新了我的帖子,并且添加了一个新代码来检查流是否已关闭或未被占用,并且您有权利。谢谢 – Aguid

+1

更准确地说,在这两种情况下(IO或集合流),在您的示例中的方法调用情况下,流实例将在方法返回后清除**(它并不真正专用于GC操作,方法调用在返回后丢弃的堆栈上工作)。无论在方法中抛出或不抛出异常,堆栈中的局部变量都被清除。 (1/2) – davidxxx

+1

它们之间的区别在于使用资源(IO)的流将调用可能会锁定文件或维护连接等的方法....因此,在这种特定情况下,作为操纵资源返回的方法,你一定会清除这些可关闭资源上的所有可能的锁。(2/2) – davidxxx

1

按照docs,是推荐(更安全)关闭使用在这种情况下从流返回的try-与资源:

如果需要的文件系统资源的及时处置,将 尝试,机智应该使用h-resources结构来确保在流操作完成后 之后调用流的close方法。

在其它情况下,其中该Stream从阵列或列表馈送,这都没有区别。

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 (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-与资源, 的代码只是噪音不必要的偏执狂,所以它是没有尝试与 - 资源实际上更好。

+0

@Kayaman真的,我澄清了这一点,谢谢 – janos

+0

我更新了我的帖子,看看这个流是否已经自动关闭(请参阅resourceWithoutTry()方法),看起来我的流仍然打开! – Aguid

+0

无论您的消费者是否抛出异常,'forEach'都不会关闭流。除此之外,您应该始终使用流方法的返回值,而不是假定该方法修改了流的内部状态。或者只是使用链接,它本质上使用返回值:'Stream.of(1,2,3).onClose(() - > System.out.println(“close called”)).forEach(x-> {/*不需要扔* /}); //将永远不会打印邮件...... – Holger

相关问题