2012-04-01 92 views
2

我只是想看看是否有更好的方法我應該處理這個。我對流的理解是,只要關閉流,封裝在其中的任何流都將被關閉,這就是爲什麼我最終只關閉TarArchiveOutputStream的原因。如果我在rawDir或archiveDir上獲得FileNotFound,我想記錄它,否則我想拋出其他任何東西。與嘗試的Java,異常處理和關閉流,終於

public static void createTarGzOfDirectory(File rawDir, File archiveFile) throws IOException { 
    FileOutputStream fOut = null; 
    BufferedOutputStream bOut = null; 
    GzipCompressorOutputStream gzOut = null; 
    TarArchiveOutputStream tOut = null; 
    try { 
     fOut = new FileOutputStream(archiveFile); 
     bOut = new BufferedOutputStream(fOut); 
     gzOut = new GzipCompressorOutputStream(bOut); 
     tOut = new TarArchiveOutputStream(gzOut); 
     addFileToTarGz(tOut, rawDir, ""); 
    } catch (FileNotFoundException e) { 
     log.error("File not found: " + e); 
    } finally { 
     if(tOut != null) { 
      tOut.finish(); 
      tOut.close(); 
     } 
    } 

任何其他考慮或改善事情的想法?

+0

爲什麼你認爲你想打電話給'完成'? – bmargulies 2012-04-01 22:40:39

回答

3

我流的理解是,只要你關閉流,其內包裹的任何流將被關閉...

這是正確的。

但是,您的代碼(有效地)假設如果tOutnull,則鏈中的其他流均未創建。這是一個有點狡猾的假設。考慮這個序列:

  1. FileOutputStream已創建並指定給fOut
  2. BufferedOutputStream被創建並且被分配給bOut
  3. GzipCompressorOutputStream構造函數拋出異常或錯誤。 (也許堆滿了......)。
  4. catch被跳過......錯了異常。
  5. finally檢查tOut,發現它是null,並且什麼都不做。

最終結果:我們泄漏了FileOUtputStream所擁有的文件描述符/通道。

獲得這個例子(絕對)正確的關鍵是理解那些流對象中的哪些持有關鍵資源,並確保THAT流被關閉。其他不擁有資源的流不必關閉。

} finally { 
    if (fOut != null) { 
     fOut.close(); 
    } 
} 

的另一點是,你需要將tOut.finish()呼叫轉移到tryaddFileToTarGz調用之後。

  • 如果addFileToTarGz調用拋出一個異常,或者如果你沒有走到這一步,該finish通話是在浪費時間。

  • finish調用將嘗試將索引寫入歸檔,並且THAT可能會拋出IOException。如果在finally塊中發生這種情況,那麼finally代碼塊中關閉流鏈的任何以下代碼都不會執行......並且文件描述符將被泄漏。

+1

對null的檢查無意中增加了複雜性。在'try'之前,在聲明處初始化'fOut'。 – erickson 2012-04-01 23:30:29

+0

@erikson - 我知道這一點。但是,如果你這樣做,你將需要另一個封閉的try-catch來報告FileNotFound異常。我的回答是關注正確性,而不是最優雅的問題解決方案。 – 2012-04-02 00:26:42

+1

通常,在此級別處理異常時,它們處理不正確。由於該方法拋出'IOException',因此不會「捕捉」任何東西。這不是真正的「優雅」;更簡單的代碼更容易。 – erickson 2012-04-02 00:39:47

0

你可以鏈中的所有構造函數你在一起,就像這樣:

tOut = new TarArchiveOutputStream(new GzipCompressorOutputStream(new BufferedOutputStream(new FileOutputStream(archiveFile)))); 

,並保存自己6行初始化和調試3個局部變量。並不是每個人都喜歡用這種方式鏈接事物 - 我個人認爲它更具可讀性,但其他人可能更喜歡它。

至於關閉流,它看起來正確的給我。

+3

如果GzipCOmpressorOutpuStream構造函數拋出異常,會發生什麼情況?你如何關閉其他的? – 2012-04-01 22:46:42

+0

@GuillaumePolet - 理論上說是泄漏。在實踐中,你必須考慮什麼可能會導致拋出異常,並決定是否可能重複發生。 – 2012-04-01 23:15:04

+0

@StephenC同意。但即使它只能發生幾次,我認爲你應該預見這些情況,並關閉流。如果沒有任何中間構造函數可以拋出異常,當然這只是浪費時間 – 2012-04-01 23:18:21

1

雖然它看起來醜陋,是的,也許,不太可能的情況下,就應該全部關閉級聯。是的,如果你關閉TarArchiveOutputStream,它應該關閉潛在的流。但是,根據實施情況,情況可能並非總是如此。此外,也許主要是,如果其中一箇中間構造函數拋出異常,tOut將爲空,但其他的可能不會。這意味着你的流是打開的,但你沒有關閉任何。