2008-10-01 228 views
30

好吧,我一直在做如下(變量名稱已更改):關閉一個Java的FileInputStream


FileInputStream fis = null; 
try 
{ 
    fis = new FileInputStream(file); 

    ... process ... 

} 
catch (IOException e) 
{ 
    ... handle error ... 
} 
finally 
{ 
    if (fis != null) 
     fis.close(); 
} 
 

最近,我開始使用FindBugs的,這表明我沒有正確關閉流。我決定查看是否有任何可以通過finally {}塊來完成,然後我看到,哦,是的,close()可以拋出IOException。人們應該在這裏做什麼? Java庫引發了太多的檢查異常。

+0

`fis`不可能在你測試的時候爲空。它可以在缺失的`finally`塊中爲null,在那裏你應該測試它並關閉它。但是自引入「試用資源」語法以來,這個問題已經過時了。 – EJP 2015-04-28 01:14:58

+0

我修改了相應的代碼,以免人們誤導。 – 2015-04-28 14:23:57

回答

38

對於Java 7和以上try-with-resources應使用:

try (InputStream in = new FileInputStream(file)) { 
    // TODO: work 
} catch (IOException e) { 
    // TODO: handle error 
} 

如果你被困在Java 6或以下...

這種模式避免了與碴圍:

try { 
     InputStream in = new FileInputStream(file); 
     try { 
      // TODO: work 
     } finally { 
      in.close(); 
     } 
    } catch (IOException e) { 
     // TODO: error handling 
    } 

對於更詳細的關於如何有效地與密切處理,閱讀這篇博客:Java: how not to make a mess of stream handling。它有更多的樣本代碼,更多的深度和覆蓋關閉捕獲塊的陷阱。

25

像下面這樣的東西應該做到這一點,不管你在試圖關閉流時是拋出還是吞下IOException。

FileInputStream fis = null; 
try 
{ 
    fis = new FileInputStream(file); 

    ... process ... 


} 
catch (IOException e) 
{ 
    ... blah blah blah ... 
} 
finally 
{ 
    try 
    { 
     if (fis != null) 
      fis.close(); 
    } 
    catch (IOException e) 
    { 
    } 
} 
4

你也可以使用一個簡單的靜態輔助方法:

public static void closeQuietly(InputStream s) { 
    if (null == s) { 
     return; 
    } 
    try { 
     s.close(); 
    } catch (IOException ioe) { 
     //ignore exception 
    } 
} 

,並使用該從你的finally塊。

+0

那麼,這看起來像邀請NullPointerException ... – 2008-10-01 07:22:48

+0

感謝您的評論,相應地改變了它 – squiddle 2008-10-07 15:35:58

0

希望有一天我們會在Java中關閉,然後我們將失去很多冗長。

所以相反,在javaIO中會有一個可以導入的輔助方法,它可能需要一個「Closable」接口和一個塊。裏面是輔助方法嘗試{closable.close()}趕上(IOException異常前){//等等}被定義一勞永逸,然後你就可以寫

Inputstream s = ....; 
withClosable(s) { 
    //your code here 
} 
3

沒什麼補充,除了一個非常小的文體建議。 自編文件代碼的典型示例適用於這種情況 - 爲忽略的IOException提供一個描述性變量名稱,您必須在close()上記錄該變量名稱。

所以squiddle的回答變成了:

public static void closeQuietly(InputStream s) { 
    try { 
     s.close(); 
    } catch (IOException ignored) { 
    } 
} 
-4

你關心的主要是從獲得的FindBugs或與具有工作代碼乾淨的報告?這些不一定是一回事。你的原始代碼是好的(儘管我會擺脫多餘的if (fis != null)檢查,因爲否則將會拋出OutOfMemoryException)。 FileInputStream有一個終結器方法,它會在你處理中實際收到IOException的情況下爲你關閉流。它只是不值得費心使代碼更復雜,以避免你得到一個IOException和

  • 這種情況經常是你開始運行到終結積壓問題

    1. 極端情況不太可能發生的。

    編輯:如果你得到這麼多的IOExceptions您正在運行與終結隊列問題,那麼你已經遠遠更大的魚魚苗!這是關於獲得一種觀點。

  • 2

    在大多數情況下,我發現這是隻有更好趕上IO異常,只需使用try-最後:

    final InputStream is = ... // (assuming some construction that can't return null) 
    try { 
        // process is 
        ... 
    } finally { 
        is.close(); 
    } 
    

    除了FileNotFoundException,你一般不能「解決」一個IOException。剩下的唯一要做的就是報告一個錯誤,並且通常會在調用堆棧中進一步處理,所以我發現傳播異常更好。

    由於IOException是一個檢查異常,您將不得不聲明此代碼(及其任何客戶端)throws IOException。這可能太吵了,或者你可能不想透露使用IO的實現細節。在這種情況下,可以用一個異常處理程序來包裝整個塊,該異常處理程序將IOException包含在RuntimeException或抽象異常類型中。

    詳情:我知道的是,上述代碼吞下從try塊任何異常時在finally塊的close操作產生IOException。我不認爲這是一個大問題:通常,try塊的例外情況與IOException的例外情況相同,導致close失敗(即,IO很難正常工作,然後在關閉時失敗) 。如果這是一個問題,那麼可能需要麻煩來「平息」收盤價。

    1

    以下解決方案在關閉失敗時正確引發異常,而不會在關閉前隱藏可能的異常。

    try { 
        InputStream in = new FileInputStream(file); 
        try { 
         // work 
         in.close(); 
        } finally { 
         Closeables.closeQuietly(in); 
        } 
    } catch(IOException exc) { 
        // kernel panic 
    } 
    

    這是可行的,因爲第二次關閉has no effect

    這依靠番石榴Closeables,但如果喜歡,可以編寫自己的closeQuietly方法,如squiddle(另請參閱serg10)所示。

    在一般情況下報告關閉錯誤很重要,因爲close可能會向流中寫入一些最終字節,例如,因爲緩衝。因此,您的用戶想知道它是否失敗,或者您可能想採取某種行動。當然,這在FileInputStream的特定情況下可能不是真的,但我不知道(但由於已經提到的原因,我認爲最好報告一個關閉錯誤,如果它發生的話)。

    由於嵌入式try塊的結構,上面的代碼有點難以理解。它可能被認爲是更清晰的兩個方法,一個拋出一個IOException和一個捕獲它的方法。至少這是我會選擇的。

    private void work() throws IOException { 
        InputStream in = new FileInputStream(file); 
        try { 
         // work 
         in.close(); 
        } finally { 
         Closeables.closeQuietly(in); 
        } 
    } 
    
    public void workAndDealWithException() { 
        try { 
         work(); 
        } catch(IOException exc) { 
         // kernel panic 
        } 
    } 
    

    基於http://illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make-mess-of-stream.html(由McDowell引用)。

    10

    您可以使用添加了JDK7的try-with-resources功能。正是建立應對這樣的事情

    static String readFirstLineFromFile(String path) throws IOException { 
        try (BufferedReader br = new BufferedReader(new FileReader(path))) { 
        return br.readLine(); 
        } 
    } 
    

    的documenation說:

    試戴與資源語句確保每個資源在發言結束時關閉 。