Java編碼中異常的使用建議

source_code.jpg

異常酝静,精確計算機世界里的不安定份子。當然說的是異常的使用狼忱,似乎沒有什么統(tǒng)一的標準膨疏。但是我覺得在一個團隊或者是系統(tǒng)里面還是應該遵循一定的規(guī)矩。

無規(guī)矩不方圓藕赞。

下面列一下目前工作中總結的一些使用經(jīng)驗

首先看下Java異常的分類。

Java 異常的分類

  • 受檢異常 (Checked Exception). 異常的處理由編譯器來保證卖局,如果方法聲明異常但是沒處理則編譯失敗斧蜕。
  • 非受檢異常 (Unchecked Exception). 編譯器不會檢查的一類異常
    • RuntimeException. IndexOutOfBoundsException, IllegalArgumentException 等
    • Error. 表示很嚴重的問題砚偶,應用自己也搞不定批销, 與代碼編寫者無關。 OutOfMemoryError 等染坯。 不要繼承Error來定義異常

異常機制提供了一種異常事件的冒泡處理機制均芽,你可以選擇你想處理這個異常的層次。 如果沒有異常的話单鹿,你就要在很低的層次處理異常掀宋,并想辦法通知上層

異常使用中的困惑

在我的使用或者團隊大家的共識是,異常使用困惑的地方主要集中在下面兩點:

  • 什么時候使用受檢異常仲锄,什么時候使用非受檢異常
  • 錯誤碼和異常的使用

所以接下來主要圍繞著兩個困惑來進行解答

先來看看受檢和非受檢異常使用的時機劲妙,此處非受檢指的是 RuntimeException

受檢異常的使用時機

Use checked exceptions for conditions from which the caller
can reasonably be expected to recover

  • 當可以用來表示業(yè)務流轉中的異常分支儒喊,給出更合理的錯誤提示時
  • 當你覺得調用方可以解決異常時
  • 當異常由外部不可控的因素導致時镣奋,例如用戶輸入數(shù)據(jù)庫異常怀愧, 文件不存在侨颈, 網(wǎng)絡

當你面臨上面的情景時可以采用受檢異常

非受檢異常的使用時機

Use runtime exceptions to indicate programming errors (Bugs) - From Effective Java

RuntimeException 通常指程序運行時的錯誤余赢,可以引申為調用方錯誤的使用了API或者類庫設計者的問題。

Runtime exceptions can occur anywhere in a program, and in a typical one they can be very numerous. Having to add runtime exceptions in every method declaration would reduce a program's clarity. Thus, the compiler does not require that you catch or specify runtime exceptions - From Oracle The Java? Tutorials

上面說到RuntimeException是程序中非常常見的錯誤哈垢,因此沒有強制讓編譯器來檢查這一類錯誤妻柒。但是程序員自己就需要做好預先檢查的工作。RuntimeException 有個好處就是能夠穿透到最上層温赔,讓最上層做決定。因此可以圍繞下面幾點來使用RuntimeException

  • 當你處理不了底層聲明的異常時陶贼,將底層異常轉換為RuntimeException啤贩。
  • 當用戶調用的參數(shù)不符合程序預期,使用不當時

    一個強有力的證明: IllegalArgumentException 定義的是 RuntimeException

錯誤碼和異常使用的時機

在沒有異常機制的程序語言中拜秧,可能比較依賴錯誤碼等來作為業(yè)務層面的流程判斷痹屹。但是在Java中提供了異常的處理機制,這種冒泡處理機制枉氮,讓你可以選擇你想處理這個異常的層次志衍。 如果沒有異常的話,你就要在很低的層次處理異常聊替,并想辦法通知上層楼肪。

總結來講,錯誤碼和異常的特點就是

  • 異常是強類型且類型安全的分支控制技術
  • 返回錯誤值則是弱類型不安全

作為Java程序員惹悄,應該優(yōu)先使用異常來反饋程序中的異常情況春叫。

使用異常來區(qū)分正常的業(yè)務場景和錯誤的業(yè)務路徑

    public String foo() IOException, FileNotFoundException {
        ...
    }

    // 一個好的使用方法應該是這樣
    try {
        foo();
    } catch (IOException ioe) {
        // 給出友好的提示信息
    } catch (FileNotFoundException fe) {
        // 給出友好的提示信息        
    }

一種異常和錯誤碼的混合方案

定義一個業(yè)務異常,然后異常中添加字段來表示錯誤碼泣港。

enum ErrorCode {
    INVALID_PARAM, INVALID_PATH, INVALID_STAUS;
}

class MyException extend Exception {
    public MyException(String msg) {
        super(msg);
    }
}

// 使用場景
public String foo() throws MyException {
    if (...) {
        throw new MyException(ErrorCode.INVALID_PARAM.getName());
    }

    ...

    if (...) {
        throw new MyException(ErroCode.INVALID_PATH.getName());
    }

    ...

    if (...) {
        throw new MyException(ErrorCode.INVALID_STATUS));
    }
}


錯誤碼和異常使用的建議

  • 同構系統(tǒng)里優(yōu)先使用異常來表示錯誤暂殖。
    • 比如兩個Java類相互調用,方法之間就可以用異常來處理非正常情況当纱,這樣可以充分發(fā)揮異常類型的作用
  • 異構系統(tǒng)的情況使用返回錯誤信息的方式呛每。
    • 比如提供一個webservice接口,就可以用不同的業(yè)務編碼來表示錯誤情況

最后是異常使用的一些實踐

異常處理的實踐

  • 記得釋放資源坡氯。特別是數(shù)據(jù)庫還有網(wǎng)絡資源晨横,使用 try-catch-finally
  • 不要使用異常作控制流程之用
  • 不要忽略異常
  • 生產(chǎn)代碼不要 printStatckTrace()
  • 盡量縮小異常的捕獲范圍

具體來講就是

try-catch-finally 釋放資源

OutputStreamWriter out = ... 
java.sql.Connection conn = ... 
try { 
    ...
} catch(SQLException sqlex) { 
    ...
} catch(IOException ioex) { 
    ...
} finally { 
    if (conn != null) { 
        try { 
            conn.close(); 
        } catch(SQLException sqlex2) { 
            ...
        } 
    } 

    if (out != null) { 
        try { 
            out.close(); 
        } catch(IOException ioex2) { 
        ...
        } 
    } 
}

不要將異常用作控制流(if-else)

雖然異常本身具有流程控制的屬性,但是直接作為類似 if-else這樣的方式來使用還是不應該的

public void check(String filePath) FileNotFoundException, FileExistException  {
    ...
} 

// 使用
try {
    check();
} catch (FileNotFoundException fne) {
    // do-file not found things ..
} catch (FileExistException fee) {
    // do-file found things
}

不要忽略異常

  • 可以只打個日志箫柳,但是不要什么都不做
  • 如果你什么都做不了颓遏,就不要抓或者轉換為RuntimeException
try {
    Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException ex) {} //忽略的異常,這樣要不得滞时。出了問題坑自己叁幢、坑隊友
    
// 接收異常
try {
    ...
} catch (Exception e) {  // 過分泛華的異常,不利于排查問題
    ...
}

// 拋出異常
try {
    ...
} catch (IOException ioe) {
    throw new Exception(ioe); // 泛化了異常坪稽, 外層調用丟失了異常類型的優(yōu)勢
}

// 自定義異常
try {
    ...
} catch (SqlException sqle) {
    throw new MyOwnException(); // 定義了新的異常曼玩,但是丟了原始異常信息
}

生產(chǎn)代碼不要 printStackTrace();

// 不好的方式
try {
    ... 
} catch (IOException e) {
    e.printStackTrace();
}

try {
    ...
} catch (IOException e) {
    logger.error("message here" + e);
}

try {

} catch (IOException e) {
    logger.error("message here" + e.getMessage());
}

// 比較好的方式
try {
    ...
} catch (IOException e) {
    logger.error("message here", e);
}

異常的捕獲范圍

  • 循環(huán)的場景鳞骤,注意try代碼塊的范圍
// bad case
try {
    while(rs.hasNext()) {
        foo(rs.next());
    }
} catch (SomeException se) {
    ...
}

// good case
while(rs.hasNext()) {
    try {
        foo(rs.next());
    } catch (SomeException se) {
        ...
    }
} 

參考資料

http://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html

http://stackoverflow.com/questions/6115896/java-checked-vs-unchecked-exception-explanation

http://www.javapractices.com/topic/TopicAction.do?Id=129

http://www.ibm.com/developerworks/cn/java/j-lo-exception/index.html

http://www.blogjava.net/freeman1984/archive/2013/07/26/148850.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市黍判,隨后出現(xiàn)的幾起案子豫尽,更是在濱河造成了極大的恐慌,老刑警劉巖顷帖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件美旧,死亡現(xiàn)場離奇詭異,居然都是意外死亡贬墩,警方通過查閱死者的電腦和手機榴嗅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來陶舞,“玉大人嗽测,你說我怎么就攤上這事≈追酰” “怎么了唠粥?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長停做。 經(jīng)常有香客問我晤愧,道長,這世上最難降的妖魔是什么蛉腌? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任官份,我火速辦了婚禮,結果婚禮上眉抬,老公的妹妹穿的比我還像新娘贯吓。我一直安慰自己懈凹,他們只是感情好蜀变,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著介评,像睡著了一般库北。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上们陆,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天寒瓦,我揣著相機與錄音,去河邊找鬼坪仇。 笑死杂腰,一個胖子當著我的面吹牛,可吹牛的內容都是我干的椅文。 我是一名探鬼主播喂很,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼惜颇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了少辣?” 一聲冷哼從身側響起凌摄,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎漓帅,沒想到半個月后锨亏,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡忙干,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年器予,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片豪直。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡劣摇,死狀恐怖,靈堂內的尸體忽然破棺而出弓乙,到底是詐尸還是另有隱情末融,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布暇韧,位于F島的核電站勾习,受9級特大地震影響,放射性物質發(fā)生泄漏懈玻。R本人自食惡果不足惜巧婶,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望涂乌。 院中可真熱鬧艺栈,春花似錦、人聲如沸湾盒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽罚勾。三九已至毅人,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間尖殃,已是汗流浹背丈莺。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留送丰,地道東北人缔俄。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親俐载。 傳聞我的和親對象是個殘疾皇子铐懊,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內容