2017-11-04 221 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

相關問題