Java進階——詳談Exception

寫在最前

最近筆者在撰寫JavaWeb與自動化相結(jié)合的教程蔫缸,上篇入口在這里腿准,第二篇還在創(chuàng)作中,在發(fā)布之前拾碌,讓我們先來討論一個Java的重要技能吐葱,Exception街望。

實現(xiàn)程序的運行是所有初級的程序員所追求的,Thinking in Java 因此成為了很適合入門的一本書弟跑,然而隨著代碼行數(shù)的累積它匕,越來越多的坑也隨之到來。此時窖认,對基礎(chǔ)知識更深層次的理解就尤為關(guān)鍵豫柬。在JavaWeb與自動化結(jié)合的應用中,無腦拋出異常會導致代碼的冗余與羸弱扑浸,今天發(fā)的這篇文章將仔細地對Exception的運用進行分析烧给。

需要注意的是,本篇文章并不是對如何拋出異常的基礎(chǔ)進行講解喝噪,需要讀者對Exception機制有一定了解础嫡,文中部分用例來自Effective Java非竿,在這里同時向讀者推薦這本書作為Java進階的重要工具震庭,文末附錄中有筆者Exception部分的英文筆記供大家參考祈远。

使用Exception的情景

不要在類似迭代的循環(huán)中使用Exception捏境,尤其是涉及ArrayIndexOutOfBounds,如下所示:

try {
    int i = 0;
    while(true)
        array[i++].doSomething();
} catch(ArrayIndexOUtOfBoundsException e) {

}

主要因為此時使用try-catch有三點顯而易見的壞處:

  • 這樣做違背于JVM設(shè)置exception處理的原則贞言,JVM會花費更多的時間來處理妄田。
  • 把Code放在try-catch語句中使得一些JVM運行中的優(yōu)化被封禁酱塔。
  • 規(guī)范的迭代寫法是經(jīng)過優(yōu)化的哩陕,通過JVM的內(nèi)部處理平项,避免了很多贅余的檢查機制,是更合適的選擇悍及。

如果我們在try-catch語句中調(diào)用了另一個數(shù)組闽瓢,這個數(shù)組中出現(xiàn)了ArrayIndexOutOfBounds的異常,其中的bug就會被catch exception所蒙蔽心赶。相反扣讼,標準的迭代寫法會及時的終止線程的執(zhí)行,報出錯誤并且給出追蹤錯誤的路徑讓程序員更輕松地定位bug的來源缨叫。

現(xiàn)在我們通過Java的Iterator接口來看一下標準迭代寫法椭符,在標準的迭代寫法中,我們利用hasNext()作為state-testing判斷方法弯汰,來實現(xiàn)state-dependent方法next()艰山,代碼如下:

for (Iterator<Foo> i = collection.iterator(); i.hasNext(); ) {
    Foo foo = i.next();
    //...
}

綜上所述,Exception是為了異秤缴粒或者說例外的情況而準備的,不應該在普通的語句中使用摔吏,并且程序員也不該寫出強迫他人在正常流程的語句中使用Exception的API鸽嫂。

Checked與Unchecked的區(qū)別

在Java中Throwable是Exception與Error的父類纵装,而在Effective Java書中,Throwable被分為了以下三類:

  1. Checked Exceptions
  2. Runtime Exceptions
  3. Errors

其中据某,2和3都是Unchecked Throwable橡娄,所以在我們分析Java的異常類時,從Checked與Unchecked兩個邏輯角度來分析會更加清晰癣籽。

Checked Exception指那些在編譯過程中會檢查的挽唉,這類錯誤在運行中是“可恢復的”。我們需要在寫程序時將其拋出筷狼,換而言之瓶籽,這些異常應該并不是由程序員所導致,而是類似”例行檢查“埂材。

相反塑顺,Runtime Exception指的就是程序員本身制造出來的錯誤,在文章的第一部分中我們已經(jīng)明確指出俏险,此類錯誤不應該被拋出严拒,而應該由程序員自己去修復。需要注意的是竖独,一般來說裤唠,我們自己設(shè)計的Exception應該作為Runtime Exception的直接或者間接子類。如果你對Exception理解得比較淺莹痢,暴力地把Runtime Exception的子類背下來巧骚,對debug的幫助也相當大,可以快速定位代碼中的問題格二。

Errors與Exception不同劈彪,他是與JVM相關(guān)的,當你在寫算法時看到棧溢出顶猜,那并不是你對語言的理解導致你的代碼出現(xiàn)漏洞沧奴,而是你的數(shù)據(jù)結(jié)構(gòu)使得JVM出現(xiàn)resource deficiency或invariant failures使得程序無法繼續(xù)執(zhí)行,所以看到Errors的時候长窄,我們也不應將其拋出滔吠,而是應該對代碼結(jié)構(gòu)進行修改處理。

綜上所述挠日,如果在運行中可恢復疮绷,那么我們就應該將這種Checked Exception拋出。當不清楚該如何做的時候嚣潜,拋出Runtime Exception冬骚。重要的是,不要定義既不是Checked Exception子類也不是Runtime Exception子類的Throwable,并且記得在你自定義的Checked Exception中加入方法使代碼能在運行中恢復只冻。

Checked Exception的使用技巧

我們經(jīng)常會遇到這種問題庇麦,在一個方法中,有一行代碼需要拋出Exception喜德,我們需要將他包裹在try-catch語句中山橄。在Java8之后,我們在使用此API時必須拋出這個異常舍悯,這極大地降低了我們代碼的質(zhì)量航棱。

解決這個問題最簡單的方法可能就是我們在運行此方法是不返回任何值,但是如果這樣做我們就少了很多通過此方法返回信息和數(shù)據(jù)的機會萌衬。

因此我們提供了另一種解決方式饮醇,那便是通過將需要拋出Checked Exception的方法拆為兩個方法,使其轉(zhuǎn)變?yōu)橐粋€Unchecked Exception奄薇。第一個方法通過返回一個boolean值來指明此Exception是否應該被拋出驳阎,第二個再進行剩余的操作。下面是一個轉(zhuǎn)變的簡單例子馁蒂。

包裹在try-catch中的語句:

try {
    ted.read(book);
} catch (CheckedException e) {
    //...do sth.
}

下面是改造后的代碼:

if (ted.understand(book)) {
    ted.read(book);
} else {
    //...do sth.
}

簡單來說呵晚,就是本來是再Ted”讀“這個方法中拋出他看不懂這個書的異常,但我們將其拆分為”是否理解“與“讀”兩個方法對其進行重構(gòu)沫屡,來避免try-catch的運用饵隙。

總的來說,重構(gòu)Checked Exception是為了代碼更簡潔更可靠沮脖,避免了對Checked Exception的過度使用金矛,因為過度使用會導致API對使用者很不友好。在遇到上面所說的情況時勺届,首先考慮能否使用返回值為空的方法驶俊,因為這是最直接最簡單的解決方式。

優(yōu)先使用標準庫中的Exception

使用Java庫中提供地Exception有三大好處:

  1. 使你的API更容易地被學習與使用免姿,因為大多數(shù)程序員都了解標準的異常
  2. 讓使用了你的API的程序閱讀起來更輕松
  3. 更少地占用內(nèi)存并且更快地對Class進行加載(JVM)

不要直接重用Exception, RuntimeException, Throwable或是Error這些父類饼酿,常用的Exception在下表中列出。

Exception 使用場景
IllegalArgumentException 不匹配的非空參數(shù)的傳遞
IllegalStateException 未初始化的對象(對象狀態(tài)不匹配)
NullPointerException 在未預期的情況下遭遇空指針
IndexOutOfBoundsException 索引參數(shù)超出范圍
ConcurrentModificationException 多線程對同一個對象進行修改
UnsupportedOperationException 此對象不支持對此方法的引用

需要注意的是胚膊,重用的Exception一定要與記錄的語義一致故俐,在文檔中詳細說明,并不只是簡單地匹配Exception的名字紊婉。

結(jié)語

除了上面詳述的幾點外药版,還要注意的是,首先喻犁,每個方法拋出的異常都要有文檔槽片。其次何缓,保持異常的原子性。最重要的是筐乳,千萬不要在catch中忽略掉捕獲到的異常歌殃。

關(guān)于異常處理對于很多人來說只是Alt+Enter乔妈,但是在代碼優(yōu)化階段經(jīng)常很讓人頭疼蝙云,希望本文能使大家有所啟發(fā),對于接下來教程中的一些代碼有更好的理解路召,也歡迎大家提問勃刨,共同提高。

附錄:Effective Java 讀書筆記

Chapter 10 EXCEPTIONS

Item 69: Use exceptions only for exceptional conditions

Do not use try catch to handle your loop, it might mask the bug and is also very slow.

Exceptions are, as their name implies, to be used only for exceptional conditions; they should never be used for ordinary control flow and do not write APIs that force others to do so.

A well designed API must not force its clients to use exceptions for ordinary control flow.

In iteration codes, one should use hasNext() to decide the life circle of a loop.

Item 70: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors

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

Use runtime exceptions to indicate programming errors.

All of the unchecked throwables you implement should subclass RuntimeException (directly or indirectly).

Don't define any throwables that are neither checked exceptions nor runtime exceptions.

Provide methods on your checked exceptions to aid in recovery.

Item 71: Avoid unnecessary use of checked exceptions

In Java 8, methods throwing checked exceptions can't be used directly in streams.

How to solve the problem that if a method throws a single checked exception, this exception is the sole reason the method must appear in a try block and can't be used directly in streams?

The easiest way to eliminate this is to return an optional of the desired result type.

You can also turn a checked exception into an unchecked exception by breaking the method that throws the exception into two methods, the first of which returns a boolean indicating whether the exception would be thrown.

Item 72: Favor the use of standard exceptions

The Java libraries provide a set of exceptions that covers most of the exceptions-throwing needs of most APIs.

Benefits: makes your API easier to learn because it matches the established conventions, makes programs using your API easier to read, a smaller memory footprint and less time spent loading classes.

Do not reuse Exception, RuntimeException, Throwable, or Error directly.

Reuse must be based on documented semantics, not just on name.

Item 73: Throw exceptions appropriate to the abstraction

Higher layers should catch lower-level exceptions and, in their place, throw exceptions that can be explained in terms of the higher-level abstraction, aka. Exception Translation.

While exception translation is superior to mindless propagation of exceptions from lower layers, it should not be overused.

If it is not feasible to prevent or to handle exceptions from lower layers, use exception translation, unless the lower-level method happens to guarantee that all of its exceptions are appropriate to the higher level.

Item 74: Document all exceptions thrown by each method

Always declare checked exceptions individually, and dovument precisely the conditions under which each one is thrown using @throws tag.

Use the Javadoc @throws tag to document each exception that a method can throw, but do not use the throws keyword on unchecked exceptions.

If an exception is thrown by many methods in a class for the same reason, you can document the exception in the class's documentation comment.

Item 75: Include failure-capture information in detail messages

To capture a failure, the detail message of an exception should contain the values of all parameters and fields that contributed to the exception.

Do not include passwords, encryption keys, and the like in detail messages.

Item 76: Strive for failure atomicity

A failed method invocation should leave the object in the state that it was in prior to the invocation.

Item 77: Don't ignore exceptions

An empty catch block defeats the purpose of exceptions.

If you choose to ignore an exception, the catch block should contain a comment explaining why it is appropriate to do so, and the variable should be named ignored.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末股淡,一起剝皮案震驚了整個濱河市身隐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌唯灵,老刑警劉巖贾铝,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異埠帕,居然都是意外死亡垢揩,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門敛瓷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叁巨,“玉大人,你說我怎么就攤上這事呐籽》嫔祝” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵狡蝶,是天一觀的道長庶橱。 經(jīng)常有香客問我,道長贪惹,這世上最難降的妖魔是什么苏章? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮馍乙,結(jié)果婚禮上布近,老公的妹妹穿的比我還像新娘。我一直安慰自己丝格,他們只是感情好撑瞧,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著显蝌,像睡著了一般预伺。 火紅的嫁衣襯著肌膚如雪订咸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天酬诀,我揣著相機與錄音脏嚷,去河邊找鬼。 笑死瞒御,一個胖子當著我的面吹牛父叙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肴裙,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蜻懦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宛乃,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎征炼,沒想到半個月后析既,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡渡贾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年雄右,在試婚紗的時候發(fā)現(xiàn)自己被綠了空骚。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡擂仍,死狀恐怖囤屹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情肋坚,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布智厌,位于F島的核電站盲赊,受9級特大地震影響铣鹏,放射性物質(zhì)發(fā)生泄漏哀蘑。R本人自食惡果不足惜葵第,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望卒密。 院中可真熱鬧棠赛,春花似錦哮奇、人聲如沸恭朗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽律罢。三九已至,卻和暖如春误辑,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背巾钉。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留潦匈,地道東北人。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓茬缩,卻偏偏與公主長得像吼旧,于是被迫代替她去往敵國和親凰锡。 傳聞我的和親對象是個殘疾皇子圈暗,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

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

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,346評論 0 10
  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom閱讀 2,701評論 0 3
  • 晨時员串, 猶欲還雨, 單坐里昵济,問時雨下智绸。 三兩滴秋水穿窗落地野揪,幾處生花。 步入中庭瞧栗, 怎知他斯稳,乍還暖, 只得觀雨...
    華尊依月閱讀 310評論 2 10
  • 俄羅斯花樣滑冰選手米哈伊爾·科爾亞達接受采訪表示憎茂,不用因為節(jié)目中的錯誤而感到失望,負分也是分數(shù)竖幔。 “我明白我們在錯...
    云游四方的旅人閱讀 458評論 0 0
  • 今天幫丫頭聽寫生字是偷,我看她寫的字還可以拳氢〉懊可她看不過眼,都用橡皮擦了重新寫了一遍刺啦。要求真嚴格啊。孩子玛瘸,為你的態(tài)度點贊。
    91b6f8355762閱讀 131評論 0 1