處理Java異常的9個(gè)最佳實(shí)踐

Java的異常處理不是一個(gè)簡(jiǎn)單的話題。 初學(xué)者很難理解析恋,甚至經(jīng)驗(yàn)豐富的開發(fā)人員也可能花幾個(gè)小時(shí)討論何時(shí)應(yīng)該拋出異郴或處理異常。

這就是為什么大多數(shù)開發(fā)團(tuán)隊(duì)都有自己使用異常的規(guī)則系任。 如果你是一個(gè)新手恳蹲,你可能會(huì)驚訝于這些規(guī)則與你之前使用過的規(guī)則完全不同虐块。

無論規(guī)則如何不同,或多或少都遵循以下規(guī)則嘉蕾。 以下是幫助你入門或改進(jìn)異常處理的9個(gè)最佳實(shí)踐贺奠。

1.在finally塊中的回收資源或使用Try-With-Resource語(yǔ)句

經(jīng)常在try中使用資源,比如InputStream 错忱,之后需要關(guān)閉它儡率。 這些情況下一個(gè)常見錯(cuò)誤是在try塊結(jié)束時(shí)關(guān)閉資源。

public void doNotCloseResourceInTry() {
    FileInputStream inputStream = null;
    try {
        File file = new File("./tmp.txt");
        inputStream = new FileInputStream(file);
        // use the inputStream to read a file
        // do NOT do this
        inputStream.close();
    } catch (FileNotFoundException e) {
        log.error(e);
    } catch (IOException e) {
        log.error(e);
    }
}

如果沒有拋出異常航背,這種方法似乎完全正常喉悴。 try塊中的所有語(yǔ)句都將被執(zhí)行,資源將被關(guān)閉玖媚。

如果有異常被拋出箕肃。 這意味著你可能無法到達(dá)try塊的末尾。 因此今魔,資源無法被關(guān)閉勺像。

所以,你應(yīng)該將所有清理代碼放入finally塊或使用try-with-resource語(yǔ)句错森。

使用Finally塊

與try塊的最后幾行相比吟宦,finally塊始終被執(zhí)行。 這可以在成功執(zhí)行try塊之后或在catch塊中處理異常之后發(fā)生涩维。 因此殃姓,所有已打開的資源可以確保被清理。

public void closeResourceInFinally() {
    FileInputStream inputStream = null;
    try {
        File file = new File("./tmp.txt");
        inputStream = new FileInputStream(file);
        // use the inputStream to read a file
    } catch (FileNotFoundException e) {
        log.error(e);
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
                log.error(e);
            }
        }
    }
}

Java 7的try-with-resource語(yǔ)句

另一種選擇是使用try-with-resource語(yǔ)句

前提是資源實(shí)現(xiàn)了AutoCloseable接口瓦阐, 大多數(shù)Java標(biāo)準(zhǔn)資源都實(shí)現(xiàn)了AutoCloseable接口蜗侈。 你在try子句中打開資源后,它將在try塊執(zhí)行后自動(dòng)關(guān)閉睡蟋。

public void automaticallyCloseResource() {
    File file = new File("./tmp.txt");
    try (FileInputStream inputStream = new FileInputStream(file);) {
        // use the inputStream to read a file
    } catch (FileNotFoundException e) {
        log.error(e);
    } catch (IOException e) {
        log.error(e);
    }
}

2.拋出具體的異常而不是通用的異常

異常越具體越好踏幻。 請(qǐng)記住,不熟悉代碼的同事戳杀,或者你可能在幾個(gè)月后調(diào)用你的方法并處理異常该面。

因此,請(qǐng)務(wù)必盡可能多地提供信息信卡。 這使您的API更容易理解隔缀。 您的方法的調(diào)用者將能夠更好地處理異常或通過額外的檢查來避免它 坐求。

總是嘗試找到最適合您的異常事件的類蚕泽,例如拋出NumberFormatException而不是IllegalArgumentException 。 并避免拋出通用的異常 桥嗤。

public void doNotDoThis() throws Exception {
    ...
}
public void doThis() throws NumberFormatException {
    ...
}

3.在文檔中說明你的異常

只要在方法簽名中指定了異常 须妻,就應(yīng)該在Javadoc中記錄它 。 這與前面的最佳實(shí)踐具有相同的目的:為調(diào)用者提供盡可能多的信息泛领,以便他可以避免或處理異常荒吏。

因此,請(qǐng)確保向Javadoc添加@throws聲明并描述可能導(dǎo)致異常的情況渊鞋。

/**
 * This method does something extremely useful ...
 *
 * @param input
 * @throws MyBusinessException if ... happens
 */
public void doSomething(String input) throws MyBusinessException {
    ...
}

4.使用描述性信息來定義異常類

盡可能準(zhǔn)確地描述問題绰更,并提供最相關(guān)的信息來理解異常事件。

別誤會(huì)我的意思; 你不應(yīng)該寫一段文字锡宋。 但你應(yīng)該用1-2個(gè)短句來解釋異常的原因儡湾。 這有助于您的運(yùn)營(yíng)團(tuán)隊(duì)了解問題的嚴(yán)重性,還可以讓您更輕松地分析任何服務(wù)事件执俩。

如果你拋出一個(gè)特定的異常徐钠,它的類名很已經(jīng)描述了具體錯(cuò)誤。 你就無需提供大量其他信息役首。 一個(gè)很好的例子是NumberFormatException 尝丐。 當(dāng)你的參數(shù)是一個(gè)字符串而不是整數(shù)的時(shí)候,它會(huì)被類java.lang.Long的構(gòu)造函數(shù)拋出衡奥。

try {
    new Long("xyz");
} catch (NumberFormatException e) {
    log.error(e);
}

NumberFormatException類的名稱已經(jīng)告訴你錯(cuò)誤的原因爹袁。 如果異常類的名稱看不出原因,則需要在異常內(nèi)容中提供所需的信息矮固。

17:17:26,386 ERROR TestExceptionHandling:52 - java.lang.NumberFormatException: For input string: "xyz"

5.盡可能首先捕捉最具體的異常

大多數(shù)IDE都可以幫你完成失息。 當(dāng)你嘗試首先捕獲不太具體的異常時(shí),它們會(huì)報(bào)告代碼塊無法被訪問档址。

比如盹兢,如果你首先捕獲IllegalArgumentException ,則永遠(yuǎn)不會(huì)到達(dá)處理更具體的NumberFormatException的catch塊辰晕,因?yàn)樗荌llegalArgumentException的子類蛤迎。

始終首先捕獲最具體的異常類,并將不太具體的catch塊添加到列表的末尾含友。

你可以在以下代碼段中看到此類try-catch語(yǔ)句的示例替裆。 第一個(gè)catch塊處理所有NumberFormatException ,第二個(gè)catch塊處理非NumberFormatException的IllegalArgumentException 窘问。

public void catchMostSpecificExceptionFirst() {
    try {
        doSomething("A message");
    } catch (NumberFormatException e) {
        log.error(e);
    } catch (IllegalArgumentException e) {
        log.error(e)
    }
}

6.不要捕獲Throwable

Throwable是所有異常和錯(cuò)誤的超類辆童。 你可以在catch子句中捕獲它,但你永遠(yuǎn)不應(yīng)該這樣做惠赫!

如果在catch子句中捕獲了Throwable 把鉴,它不僅會(huì)捕獲所有異常; 它還會(huì)捕獲所有錯(cuò)誤。 JVM拋出錯(cuò)誤以指示應(yīng)用程序無法處理的嚴(yán)重問題。 典型的例子是OutOfMemoryError或StackOverflowError 庭砍。 兩者都是由應(yīng)用程序無法控制的情況引起的场晶,無法處理。

所以怠缸,最好不要捕獲Throwable诗轻,除非你完全確定你需要捕獲它。

public void doNotCatchThrowable() {
    try {
        // do something
    } catch (Throwable t) {
        // don't do this!
    }
}

7.不要忽略捕捉到的異常

剛開始的時(shí)候揭北,開發(fā)人員可能非常確定這個(gè)異常永遠(yuǎn)不會(huì)發(fā)生扳炬,他甚至加上一個(gè)著名的“這將永遠(yuǎn)不會(huì)發(fā)生”的評(píng)論。

public void doNotIgnoreExceptions() {
    try {
        // do something
    } catch (NumberFormatException e) {
        // this will never happen
    }
}

可是搔体,請(qǐng)永遠(yuǎn)不要忽視異常恨樟。 你不知道代碼將來會(huì)如何變化。

你至少應(yīng)該寫一條日志消息疚俱,告訴大家不可想象的事情剛剛發(fā)生劝术。

public void logAnException() {
    try {
        // do something
    } catch (NumberFormatException e) {
        log.error("This should never happen: " + e);
    }
}

8.處理異常是,不要僅僅記錄然后就重新拋出它

這可能是最常見的錯(cuò)誤做法计螺, 你可以找到許多類似的代碼:

try {
    new Long("xyz");
} catch (NumberFormatException e) {
    log.error(e);
    throw e;
}

在發(fā)生異常時(shí)記錄異澈痪。可能會(huì)很直觀,然后重新拋出它以便調(diào)用者可以適當(dāng)?shù)靥幚硭?但這樣做會(huì)造成錯(cuò)誤被重復(fù)記錄在日志中登馒。

17:44:28,945 ERROR TestExceptionHandling:65 - java.lang.NumberFormatException: For input string: "xyz"
Exception in thread "main" java.lang.NumberFormatException: For input string: "xyz"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:589)
at java.lang.Long.(Long.java:965)
at com.stackify.example.TestExceptionHandling.logAndThrowException(TestExceptionHandling.java:63)
at com.stackify.example.TestExceptionHandling.main(TestExceptionHandling.java:58)

其他消息也不會(huì)添加任何信息匙握。 如最佳實(shí)踐#4中所述,異常消息應(yīng)描述異常事件陈轿。 堆棧跟蹤告訴您拋出異常的類圈纺,方法和行。

如果需要添加其他信息麦射,則應(yīng)捕獲異常并將其包裝在自定義異常中蛾娶。

public void wrapException(String input) throws MyBusinessException {
    try {
        // do something
    } catch (NumberFormatException e) {
        throw new MyBusinessException("A message that describes the error.", e);
    }
}

因此,如果你捕捉了異常潜秋,請(qǐng)完整的處理它蛔琅,而不是僅僅記錄然后有拋出。 如果不需要處理峻呛,就在方法簽名中拋出異常讓調(diào)用者來處理罗售。

9.保留原始異常

有時(shí)候我們捕獲系統(tǒng)異常以后,會(huì)將其包裝成自定義異常钩述。

請(qǐng)確保將原始異常被保留寨躁。 Exception類提供了接受Throwable作為參數(shù)的特定構(gòu)造函數(shù)方法。 如果你不傳遞原始異常牙勘,將丟失原始異常的堆棧跟蹤和消息职恳。

public void wrapException(String input) throws MyBusinessException {
    try {
        // do something
    } catch (NumberFormatException e) {
        throw new MyBusinessException("A message that describes the error.", e);
    }
}

總結(jié)

正如您所看到的,當(dāng)您拋出或捕獲異常時(shí),您應(yīng)該考慮許多不同的事情放钦。 其中大多數(shù)都旨在提高代碼的可讀性或API的可用性色徘。

異常是Java的錯(cuò)誤處理機(jī)制。 你應(yīng)該與同事一起討論如何使用異常最筒,以便每個(gè)人都能認(rèn)同并以相同的規(guī)則使用異常贺氓。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蔚叨,一起剝皮案震驚了整個(gè)濱河市床蜘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蔑水,老刑警劉巖邢锯,帶你破解...
    沈念sama閱讀 212,599評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異搀别,居然都是意外死亡丹擎,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,629評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門歇父,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蒂培,“玉大人,你說我怎么就攤上這事榜苫』ご粒” “怎么了?”我有些...
    開封第一講書人閱讀 158,084評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵垂睬,是天一觀的道長(zhǎng)媳荒。 經(jīng)常有香客問我,道長(zhǎng)驹饺,這世上最難降的妖魔是什么钳枕? 我笑而不...
    開封第一講書人閱讀 56,708評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮赏壹,結(jié)果婚禮上鱼炒,老公的妹妹穿的比我還像新娘。我一直安慰自己蝌借,他們只是感情好昔瞧,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,813評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著骨望,像睡著了一般硬爆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上擎鸠,一...
    開封第一講書人閱讀 50,021評(píng)論 1 291
  • 那天缀磕,我揣著相機(jī)與錄音,去河邊找鬼。 笑死袜蚕,一個(gè)胖子當(dāng)著我的面吹牛糟把,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播牲剃,決...
    沈念sama閱讀 39,120評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼遣疯,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了凿傅?” 一聲冷哼從身側(cè)響起缠犀,我...
    開封第一講書人閱讀 37,866評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎聪舒,沒想到半個(gè)月后辨液,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,308評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡箱残,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,633評(píng)論 2 327
  • 正文 我和宋清朗相戀三年滔迈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片被辑。...
    茶點(diǎn)故事閱讀 38,768評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡燎悍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盼理,到底是詐尸還是另有隱情谈山,我是刑警寧澤,帶...
    沈念sama閱讀 34,461評(píng)論 4 333
  • 正文 年R本政府宣布榜揖,位于F島的核電站勾哩,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏举哟。R本人自食惡果不足惜思劳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,094評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望妨猩。 院中可真熱鬧潜叛,春花似錦、人聲如沸壶硅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,850評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)庐椒。三九已至椒舵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間约谈,已是汗流浹背笔宿。 一陣腳步聲響...
    開封第一講書人閱讀 32,082評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工犁钟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人泼橘。 一個(gè)月前我還...
    沈念sama閱讀 46,571評(píng)論 2 362
  • 正文 我出身青樓涝动,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親炬灭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子醋粟,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,666評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • 在 Java 中,異常處理是個(gè)很麻煩的事情重归。初學(xué)者覺得它很難理解米愿,甚至是經(jīng)驗(yàn)豐富的開發(fā)者也要花費(fèi)很長(zhǎng)時(shí)間決定異常是...
    OSC開源社區(qū)閱讀 1,110評(píng)論 3 51
  • Java是一種可以撰寫跨平臺(tái)應(yīng)用軟件的面向?qū)ο蟮某绦蛟O(shè)計(jì)語(yǔ)言。Java 技術(shù)具有卓越的通用性提前、高效性吗货、平臺(tái)移植性和...
    Java小辰閱讀 342評(píng)論 0 1
  • 引言 在程序運(yùn)行過程中(注意是運(yùn)行階段,程序可以通過編譯)狈网,如果JVM檢測(cè)出一個(gè)不可能執(zhí)行的操作,就會(huì)出現(xiàn)運(yùn)行時(shí)錯(cuò)...
    Steven1997閱讀 2,417評(píng)論 1 6
  • 今日體驗(yàn):這段時(shí)間一直沒休息笨腥,并且天特別的熱工作量還大拓哺,感覺自己快要垮掉了,今天下班早脖母,早點(diǎn)休息吧士鸥,核心:身體要適...
    房傲東閱讀 46評(píng)論 0 0
  • 參加家庭能量改造課程有感 楊曉姝 2017年3月11日至12日,這兩天參加李新異老師的家庭能量改造課程谆级,收獲不少烤礁,...
    楊曉姝閱讀 803評(píng)論 0 0