2010-04-23 56 views
40

是否有治療close()例外,關閉兩個流,那麼的不那麼醜陋的方式:的Java IO醜陋的try-finally塊

InputStream in = new FileInputStream(inputFileName); 
    OutputStream out = new FileOutputStream(outputFileName); 

    try { 
     copy(in, out); 
    } finally { 
     try { 
      in.close(); 
     } catch (Exception e) { 
      try { 
       // event if in.close fails, need to close the out 
       out.close(); 
      } catch (Exception e2) {} 
       throw e; // and throw the 'in' exception 
      } 
     } 
     out.close(); 
    } 

更新:所有上面的代碼是一個嘗試,漁獲物中,由於爲警告。

FINALLY(後回答):

,並可以使用Execute Around idiom(感謝湯姆Hawtin)做一個很好的實用方法。

+6

考慮在抽象的適當水平捕獲異常,例如IOException等,而不是過於模糊的異常。 – JRL 2010-04-23 14:20:02

+1

我認爲對於這種情況,拋出哪個異常無關緊要。或者爲什麼會呢? – 2010-04-23 17:02:09

+3

請注意,您的代碼不一定關閉'in',請參閱http://stackoverflow.com/questions/2441853/java-resource-management-please-help-to-understand-findbugs-results – meriton 2010-04-27 21:11:51

回答

46

這是正確的IDOM(它工作正常):

InputStream in = null; 
    OutputStream out = null; 
    try { 
     in = new FileInputStream(inputFileName); 
     out = new FileOutputStream(outputFileName); 
     copy(in, out); 
    finally { 
     close(in); 
     close(out); 
    } 

    public static void close(Closeable c) { 
    if (c == null) return; 
    try { 
     c.close(); 
    } catch (IOException e) { 
     //log the exception 
    } 
    } 

能正常工作的原因是,拋出異常你得終於將你的最終代碼完成後拋出之前,前提是你的最後代碼本身不會拋出異常或以其他方式異常終止。

編輯:從Java 7(和Android SDK 19 - KitKat)開始,現在有一個Try with resources語法來使這個更清晰。如何處理這個問題在this question

+5

注意:這不是所有流類型的正確習慣用法。如果'OutputStream'緩衝數據('BufferedOutputStream')或寫入結束塊('ZipOutputStream'),則可能會丟失數據,並且應用程序無法處理它,因爲它會吞服異常。記錄不能替代正確的錯誤處理。 – McDowell 2010-06-15 12:09:53

+0

@McDowell,關於輸出流是一個很好的觀點(請參閱我對Adamski的回答所作的評論),但我仍然會說這是標準習慣用法。 – Yishai 2010-06-15 15:04:11

+0

由於這是公認的答案,人們會看它 - 既然您同意@McDowell的請求,請在您的回答中提及他的評論 - - 在catch塊 – 2013-05-02 21:16:26

2

我有時使用的一個技巧是定義一個名爲closeQuietly(Closeable)的方法,該方法測試它的參數是否爲null然後關閉它,忽略任何異常。但是你需要小心地關閉OutputStreams和Writers,因爲它們實際上可能會拋出異常,很重要;例如如果最終刷新失敗。

隨着Java 7的發展,情況可能會有所改善。報告指出,它將有一個新的構造,它提供了一種更簡潔的方式來處理託管資源;例如需要在完成時關閉的流。

最後,你應該知道你的例子有一個錯誤。如果方法調用打開第二個流,則第一個流將不會關閉。第二次打開需要在try區塊內完成。

31

你可以實現一個實用方法:

public final class IOUtil { 
    private IOUtil() {} 

    public static void closeQuietly(Closeable... closeables) { 
    for (Closeable c : closeables) { 
     if (c != null) try { 
      c.close(); 
     } catch(Exception ex) {} 
    } 
    } 
} 

那麼你的代碼將被簡化爲:

try { 
    copy(in, out); 
} finally { 
    IOUtil.closeQuietly(in, out); 
} 

附加

我想象還會有這樣的方法在第三方開源庫中。但是,我的首選是避免不必要的庫依賴項,除非我使用其大部分功能。因此我傾向於自己實現這樣的簡單實用方法。

+1

這有點不同,因爲例外情況不會重新發生 – 2010-04-23 14:19:46

+0

@Sam:這是一個好點;我會修改我的答案。 – Adamski 2010-04-23 14:20:43

+0

不錯的主意,一個可變關係的var-arg。 – Yishai 2010-04-23 14:21:51

1

在大多數情況下,「在」 close()方法的例外是不相關的,因此:

try { 
     copy(in, out); 
    } finally { 
    try { in.close() } catch (Exception e) { /* perhaps log it */ } 
    try { out.close() } catch (Exception e) {/* perhaps log it */ } 
    } 

它通常是不好的做法,吞下例外,但在這種情況下,我認爲這是好的。

+0

爲什麼要忽略寫出失敗的結尾該文件可以嗎? – 2011-01-29 14:32:13

+0

因爲你知道這是一個ByteArrayOutputStream? – bmargulies 2012-01-17 11:30:44

16
try { 
    final InputStream in = new FileInputStream(inputFileName); 
    try { 
     final OutputStream out = new FileOutputStream(outputFileName);  
     try { 
      copy(in, out); 
      out.flush(); // Doesn't actually do anything in this specific case. 
     } finally { 
      out.close(); 
     } 
    } finally { 
     in.close(); 
    } 
} catch (IOException exc) { 
    throw new SomeRelevantException(exc); 
} 

記住,打開一個流可能會拋出一個異常,那麼你就需要氣流出口之間的try(請不要做一些黑客涉及null秒。任何事情都有可能引發Error(這不是一個實例的Exception)。

事實證明,catchfinally應該很少共享相同的try

由於Java SE 7,你可以寫使用try-與資源以避免這麼多的缺口,這或多或少做同樣的事情,雖然有抑制異常隱藏。

try (
    final InputStream in = new FileInputStream(inputFileName); 
    final OutputStream out = new FileOutputStream(outputFileName);  
) { 
    copy(in, out); 
    out.flush(); // Doesn't actually do anything in this specific case. 
} catch (IOException exc) { 
    throw new SomeRelevantException(exc); 
} 

您可能想要使用Execute Around idiom

我相信標準的好方法是使用NIO的transferTo/transferFrom

+0

+1,這與我輸入的答案完全相同(character-for-character)! :) – 2010-04-23 14:21:32

+0

實際上很不錯 - 請您評論它的好處與接受的答案相比嗎?同樣,如果說'copy(in,out);'和'out.close();'這兩個throw都不會丟失'copy(in,out);''拋出的異常。 – 2013-05-02 23:39:29

+0

@Mr_and_Mrs_D種類。在實踐中,未緩衝的I/O流不會從'close'拋出'IOException'。即使他們拋出了'IOException',控制流也是一樣的。/Java SE 7增加了抑制異常。我不希望任何人通過抑制樹搜索找到正確的異常,這在本地沒有理論上可行,因爲沒有定義異常優先級的順序。 – 2013-05-03 10:16:54

5

你有,在普通的io,在IOUtils,一些close平靜方法。

8

Guava有非常好的IO API,消除這種需要。例如,你的例子是:

Files.copy(new File(inputFileName), new File(outputFileName)); 

更一般地,它使用的InputSupplier S和OutputSupplier S上的概念,讓InputStream S和OutputStream s到被它的實用方法中創建,允許它在他們完全控制所以它可以正確處理關閉。

此外,它有Closeables.closeQuietly(Closeable)這基本上是大多數答案建議的方法的類型。

其中的IO內容仍處於測試階段,可能會發生變化,但值得檢查甚至使用,具體取決於您的工作內容。

7

我堅信在Java 7.0中,您不需要再自己關閉流。 Language Features in Java 7

try (BufferedReader br = new BufferedReader(new FileReader(path)) { 
    return br.readLine(); 
} 
+0

當它最終被釋放時,這將會非常棒。您可以在try(...)參數中指定多個資源,JVM可以爲您關閉這些資源。 – 2010-04-23 17:18:40

+3

雖然如果'BufferedReader'構造函數失敗(不太可能,但確實發生),你將會泄漏。你也正在挑選隨機混雜的字符編碼。 – 2011-01-29 14:36:30

-1

在C#中,有using建設會自動關閉關閉的對象,當我們離開的範圍:

using(Stream s = new Stream(filename)) { 
    s.read(); 
} 

我認爲這是對Java的嘗試-finally塊的簡寫形式。 Java 6引入了Closable接口。所以,using幾乎就在那裏。當最後一步在Java 7中完成時,確實會非常棒。

0

使用

IOUtils.closeNoThrow(myInputStream);

古樸典雅。

+2

可能你的意思是[IOUtils.closeQuietly](http://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/IOUtils.html#closeQuietly(java.io.InputStream))? – 2014-09-17 22:17:01

6

由於的Java 7有寫嘗試,終於在問候塊來Closeable資源方面更好的方式。現在

可以在try關鍵字後括號內創建你的資源,像這樣:

try (initialize resources here) { 
    ... 
} 

與上面的代碼塊結束後,他們將被關閉自動finally部分不需要。

一個例子

try (
    ZipFile zf = new ZipFile(zipFileName); 
    BufferedWriter writer = Files.newBufferedWriter(outputFilePath, charset); 
) { 
    // Enumerate each entry 
    for (Enumeration entries = zf.entries(); entries.hasMoreElements();) { 
     // Get the entry name and write it to the output file 
     String newLine = System.getProperty("line.separator"); 
     String zipEntryName = ((java.util.zip.ZipEntry)entries.nextElement()).getName() + newLine; 
     writer.write(zipEntryName, 0, zipEntryName.length()); 
    } 
} 

而且for循環完成後,資源將被關閉!

0

這裏是我的回答希望更好

https://stackoverflow.com/a/35623998/2585433

try { 
    fos = new FileOutputStream(new File("...")); 
    bos = new BufferedOutputStream(fos); 
    oos = new ObjectOutputStream(bos); 
} 
catch (Exception e) { 
} 
finally { 
    Stream.close(oos,bos,fos); 
} 


class Stream { 

public static void close(AutoCloseable... array) { 
    for (AutoCloseable c : array) { 
     try {c.close();} 
     catch (IOException e) {} 
     catch (Exception e) {} 
    } 
    } 
}