Java異常處理之最佳實踐


引言:

publicvoidconsumeAndForgetAllExceptions(){
    try{
        ...some code that throws exceptions
    } catch(Exception ex){
        ex.printStacktrace();
    }
}
采用上面這種方式處理異常時,catch代碼段接管控制權,然后catch段之后代碼繼續執行,好像什么都沒有發生。
 
publicvoidsomeMethod() throws Exception{
}
該方法內可能並不會拋出異常
總結:忽略異常或隨意拋出異常是常見現象。
 

異常的本質 

廣義的講,拋出異常分三種不同的情況: 
- 編程錯誤導致的異常:譬如NullPointerException和IllegalArgumentException),代碼通常對編程錯誤沒有什么對策。 
- 客戶端的錯誤導致的異常:試圖違背制定的規則,例如讀寫文件流的關閉操作。
- 資源錯誤導致的異常:當獲取資源錯誤時引發的異常。例如,系統內存不足,或者網絡連接失敗。
 
對於Java作為第一個使用checked exception的主流面向對象語言而言,高層必須catch或throw,若不能有效的處理異常的話,就會在API和代碼之間造成負擔。
publicList getAllAccounts() throws
    FileNotFoundException, SQLException{
    ...
}
Checked exception被詬病破壞封裝性:getAllAccounts使得調用者必須處理這兩個異常,但調用者不知道什么文件找不到&什么數據庫語句失敗,也不知道該提供什么文件系統或者數據庫的事物層邏輯。這使得方法與調用者之間形成不恰當的緊耦合關系。
 
 

設計API的最佳實踐

1. 根據客戶端如何應對Exception決定使用checked exception還是unchecked exception
分析:若客戶端可以采取行動恢復的建議采用checked exception
unchecked exception建議多用,因為它不強迫客戶端必須處理,會擴散直到客戶端想處理。建議采用Java定義好的異常類,更有利於代碼的易讀性。
 
2. 保持封裝性
不要將針對某特定實現的checked exception用到更高的層次中去。
例如不要讓SQLException擴散到邏輯層去,因為邏輯層是不需要知道SQLException。解決辦法:a.客戶端有相應應對措施時,轉換SQLException為另一個checked exception
b.若客戶端無法處理,轉換SQLException為unchecked exception 4. 將異常文檔化 你可以采用Javadoc’s @throws標簽將你的API拋出的checked和unchecked exception都文檔化。

3. 不要忽略異常當一個API方法拋出checked exception時,如果對你沒什么意義,直接轉換成unchecked exception拋出,而不要僅僅用空的catch來忽略該異常。
處理示例:
之前代碼:
publicvoiddataAccessCode(){
    try{
        ..some code that throws SQLException
    }catch(SQLException ex){
        ex.printStacktrace();
    }
}
上面catch段僅僅抑制住了異常,什么都沒做,改成下面部分后,
publicvoiddataAccessCode(){
    try{
        ..some code that throws SQLException
    }catch(SQLException ex){
        thrownewRuntimeException(ex);
    }
}
此時,若發生SQLException異常,會拋給高層RuntimeException異常,使得當前線程掛起並爆出異常信息。此時邏輯層也不需要進行不必要的異常處理,尤其是邏輯層不知道怎么處理SQLException。
補充:若邏輯層打算采取恢復措施應對SQLException,那可以拋出自定義的checked exception。
 
4. 如果自定義的異常沒有提供有用的信息的話,請不要創建它們。
a.當前異常發生時,給出一些相關的數據信息
publicclassDuplicateUsernameException
    extendsException {
    publicDuplicateUsernameException
        (Stringusername){....}
    publicStringrequestedUsername(){...}
    publicString[] availableNames(){...}
}
b.若不想獲取詳細信息,拋出標准異常也可以。
thrownewException("Username already taken");
注:拋出什么異常參見最佳實踐第一點
 
 
 

使用異常的最佳實踐

1. 記得釋放資源

publicvoiddataAccessCode(){
    Connection conn = null;
    try{
        conn = getConnection();
        ..some code that throws SQLException
    }catch(SQLException ex){
        ex.printStacktrace();
    } finally{
        DBUtil.closeConnection(conn);
    }
}  
classDBUtil{
    publicstaticvoidcloseConnection
        (Connection conn){
        try{
            conn.close();
        } catch(SQLException ex){
            logger.error("Cannot close connection");
            thrownewRuntimeException(ex);
        }
    }
}
DBUtil關閉連接工具類,最重要的部分在於finally,無論異常發不發生都會執行關閉連接操作,如果關閉發生異常會拋出一個RuntimeException。
 

2. 不要使用異常作控制流程之用

public void useExceptionsForFlowControl() {
     try {
         while ( true ) {
             increaseCount();
         }
     } catch (MaximumCountReachedException ex) {
     }
     //Continue execution
}
 
public void increaseCount()
     throws MaximumCountReachedException {
     if (count >= 5000 )
         throw new MaximumCountReachedException();
}
生成棧回溯的代價很昂貴,棧回溯的價值是在於調試,而不是流程控制中。
 

3 不要catch最高層次的exception

try{
..
}catch(Exception ex){
}
上面這種寫法不建議,應該對具體異常分類處理。
 

4.盡量減小try代碼塊

 






免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM