1. 程式人生 > >Java 故障安全異常處理

Java 故障安全異常處理

tst 了解 clas 原因 fileinput 經典 錯誤 pointer 指向

異常處理代碼必須保證其故障安全機制,其中一條重要的規則如下:

try-catch-finally塊拋出的最後一個異常將會在調用堆棧中傳遞。
所有早期異常將會消失。

如果從一個catchfinally塊拋出一個異常,那麽這個異常可能會導致try塊中捕獲的異常隱藏。這會在你試圖確定異常的原因時產生誤導。

下面是non-fail-safe異常處理的經典示例:

InputStream input = null;
try {
    input = new FileInputStream( "myFile.txt" );
    /* do something with the stream */
}
catch
( IOException e ) { throw new WrapperException( e ); } finally { try { input.close(); } catch ( IOException e ) { throw new WrapperException( e ); } }

如果FileInputStream構造器拋出一個FileNotFoundException異常,你認為會發生什麽?

首先會執行catch塊,該塊只會重新拋出包裝在WrapperException中的異常。

其次將執行finally

塊來關閉輸入流。但是,由於FileInputStream構造器拋出了一個FileNotFoundException異常,引用變量"input"將為null。結果將是從finally塊中拋出NullPointerException異常。NullPointerException不會被catch ( IOException e )子句捕獲,所以它將會在調用堆棧中傳遞。而第一個 catch 塊中拋出的WrapperException將會消失。

處理這種情況的正確方式是,再調用任何方法之前,先檢查在try塊中分配的引用是否為null。比如像下面這樣:

InputStream input = null;
try
{ input = new FileInputStream("myFile.txt"); //do something with the stream } catch(IOException e) { //first catch block throw new WrapperException(e); } finally { try { if(input != null) input.close(); } catch(IOException e){ //second catch block throw new WrapperException(e); } }

但即使是這樣處理同樣還是會有問題,讓我們先假設"myFile.txt"文件存在,因此"input"引用現在指向一個有效的FileInputStream。同樣我們也假設處理輸入流時引發了異常,這時第一個 catch 子句捕獲後處理並拋出WrapperException,在將WrapperException傳遞到調用堆棧之前,還要先執行finally子句。如果input.close()調用失敗,那麽它將會拋出IOException並且被第二個 catch 子句捕獲並拋出WrapperException,這時從第一個 catch 子句中拋出的WrapperException再次消失,只有第二個 catch 拋出的WrapperException才會被傳遞到調用堆棧中。

如你所見,故障安全(fail safe)異常處理並不總是無價值的。InputStream處理示例甚至不是你可以遇到的最復雜的示例。JDBC 中的事務有更多錯誤的可能性。當嘗試提交、然後回滾,最後嘗試關閉連接時,可能會出現異常。所有這些可能出現的異常都應該由異常處理代碼來處理,因此,他們都不會使第一個被拋出異常消失。這樣做的一種方法是確保拋出的最後一個異常包含以前拋出的所有異常,這樣開發人員就可以了解錯誤原因。

BTW,Java 7 中的try-with-resources特性使得實現故障安全異常處理變得更加容易。

原文鏈接:http://tutorials.jenkov.com/java-exception-handling/fail-safe-exception-handling.html

Java 故障安全異常處理