2011-05-17 102 views
5

下面是一個例子,我有關於問題(它來自另一個SO問題):關閉流/插座的try-catch-終於

public static void writeToFile (final String filename) 
{ 
    PrintWriter out = null;  
    FileOutputStream fos = null; 

    try 
    { 
     fos = new FileOutputStream(filename); 
     out = new PrintWriter(new BufferedWriter(
           new OutputStreamWriter(fos, "UTF-8"))); 

     for (final String line : log) 
     { 
      out.println(line); 
     } 

     out.flush(); 
     out.close(); 
    } 
    catch (final Exception e) 
    { 
     System.err.println("Unable to write log to file."); 
    } 
    finally 
    { 
     if (fos != null) 
     { 
      try 
      { 
       fos.close(); 
      } 
      catch (final IOException e) 
      { 
       System.err.println("Unable to write log to file."); 
      } 
     } 
    } 
} 

現在我覺得這個代碼工作正常,並釋放所有的資源,其中應該等我的問題是:

  1. 我爲什麼要在try-catch-finallyfinally部分被關閉FileOutputStream?當然,我可以在try-catch之後連續輸入代碼?

  2. 爲什麼我必須單獨關閉FileOutputStream而不是簡單地用new OutputStreamWriter(fos, ...替換new OutputStreamWriter(new FileOutputStream(filename), ...?如果我先關閉FileOutputStream,是否會自動關閉剩餘部分並釋放資源?同樣的問題適用於套接字,如果關閉套接字連接,會自動關閉流讀寫器並釋放資源?

  3. 由於不同的系統具有不同的字符集(或沿着這些行),我已經多次被告知要確保我讀寫具有「UTF-8」的流。這是否適用於讀取/寫入RAW字節數據(比如來自非文本文件或加密的結果),因爲我認爲字符集只能處理文本字符?

+1

move code'out.close();' for out = new PrintWriter(...)最終阻止,與'fos = new FileOutputStream(filename)'的定義相同;'' – mKorbel 2011-05-17 14:59:27

+0

你寫道:「我目前找不到線程」。也許你的意思是22answers.com上的[「何時使用Java中的哪個Writer子類;常見實踐」](http://www.22answers.com/posts/answers/en/4069698)。 – 2011-05-17 15:05:34

+0

@mKorbel - 謝謝。 @Marnix Klooster - 是的,它實際上是SO上的一篇文章的副本。 @Jonathon - 感謝編輯。 – Cheetah 2011-05-17 20:20:11

回答

6
  1. 因爲如果你的代碼拋出未經處理的異常,在try-catch集團後的片段將永遠不會被執行。例如,NullPointerExcpetion就是這種情況。

  2. 你不需要。如果關閉一個流,任何封閉的流也將被關閉。

  3. 不是。只有在將字節轉換爲字符(或其他方式)時纔是這種情況。您正在指定OutputStreamWriter的字符集,它負責將字符轉換爲字節。

0

關於第一點 - 如果你的代碼拋出一個錯誤,而不是一個異常,如果收盤聲明最終不是FileOutputStream中不會被關閉。當你捕捉異常時,對你來說有點虛構,但是在更典型的情況下,如果你要清理finally塊中的資源,可以捕獲更具體的異常,那麼方法的執行可能會在它到達代碼將關閉資源。

1

爲什麼我應該在try-catch-finally的finally節中關閉FileOutputStream?

您可以將close()置於所有寫入/讀取操作塊的末尾,但如果在此處(讀取/寫入)出現問題,您將到達異常處理塊,並且不會關閉流。如果您選擇將close()置於異常處理塊並且一切正常,請猜測什麼?...沒有流將被關閉。所以你可以在兩個代碼塊中完成,但這樣代碼的可讀性就會降低。所以把它放在finally塊上可以保證你不管它將被關閉。

其次,你應該只關閉最後一個鏈接的流。所以如果你有這個。

fos = new FileOutputStream(filename); 
     out = new PrintWriter(new BufferedWriter(
           new OutputStreamWriter(fos, "UTF-8"))); 

你只需要

out.close(); 

這將關閉相關聯的出流等流。

UTF-8部分取決於您嘗試讀取的數據編碼類型,如果您編碼UTF-8,請解碼UTF-8。

+0

你確定關於「這將關閉與流出相關的其他流。」部分?這在PrintWriter類中關閉的javadoc中沒有記錄。 – MTilsted 2011-05-17 15:21:10

+0

如果在構造裝飾器時發生異常,那麼[僅]關閉最後一個流將導致泄漏。 – 2011-05-17 15:24:48

0
  1. 通常,在catch塊中,有人會拋出一個RuntimeException,這會導致此方法返回。如果你沒有在最後清理你的資源,他們將不會被清理。在你的例子中,你應該在finally塊之後很好地關閉,因爲你不會強迫你離開。
  2. 在包裝流上調用close()應該關閉子流。如果你編寫自己的包裝流,它的close方法應該把close()委託給它的子節點。這就是爲什麼我說應該由實施者調用close()來處理它的孩子。
  3. 如果您正在編寫原始字節數據,則不需要指定utf-8。這是一種字符編碼,可幫助顯示各種語言的字符。