日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Effective Java读书笔记五:异常

發(fā)布時間:2025/3/21 java 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Effective Java读书笔记五:异常 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

第57條:只針對異常的情況才使用異常

異常是為了在異常情況下使用而設(shè)計的,不要將它們用于普通的控制流,也不要編寫迫使它們這么做的API。

下面部分來自:異常

如果finally塊中出現(xiàn)了異常沒有捕獲或者是捕獲后重新拋出,則會覆蓋掉try或catch里拋出的異常,最終拋出的異常是finally塊中產(chǎn)生的異常,而不是try或catch塊里的異常,最后會丟失最原始的異常。

如果在try、catch、finally塊中都拋出了異常,只是只有一個異常可被傳播到外界。記住,最后被拋出的異常是唯一被調(diào)用端接受到的異常,其他異常都被掩蓋而后丟失掉了。如果調(diào)用端需要知道造成失幾的初始原因,程序之中就絕不能掩蓋任何異常。

請不要在try塊中發(fā)出對return、break或continue的調(diào)用,萬一無法避免,一定要確保finally的存在不會改變函數(shù)的返回值(比如說拋異常啊、return啊以及其他任何引起程序退出的調(diào)用)。因為那樣會引起流程混亂或返回值不確定,如果有返回值最好在try與finally外返回。

不要將try/catch放在循環(huán)內(nèi),那樣會減慢代碼的執(zhí)行速度。

如果構(gòu)造器調(diào)用的代碼需要拋出異常,就不要在構(gòu)造器處理它,而是直接在構(gòu)造器聲明上throws出來,這樣更簡潔與安全。因為如果在構(gòu)造器里處理異常或?qū)a(chǎn)生異常的代碼放在構(gòu)造器之外調(diào)用,都將會需要調(diào)用額外的方法來判斷構(gòu)造的對象是否有效,這樣可能忘記調(diào)用這些額外的檢查而不安全。

第58條:對可恢復(fù)的情況使用受檢異常,對編程錯誤使用運用時異常

Java程序設(shè)計語言提供了三種異常:受檢的異常(checked exception)、運行時異常(run-time exception)和錯誤(error)。關(guān)于什么時候適合使用哪種異常,雖然沒有明確的規(guī)定,但還是有些一般性的原則的。

檢測性異常通常是由外部條件不滿足而引起的,只要條件滿足,程序是可以正常運行的,即可在不修改程序的前提下就可正常運行;而運行時異常則是由于系統(tǒng)內(nèi)部或編程時人為的疏忽而引起的,這種異常一定要修正錯誤代碼后再能正確運行。受檢異常對客戶是有用的,而運行時異常則是讓開發(fā)人員來調(diào)試的,對客戶沒有多大的用處。

在決定使用受檢異常還是未受檢異常時,主要的原則是:如果期望調(diào)用者能夠適當(dāng)?shù)鼗謴?fù),對于這種情況就應(yīng)該使用受檢的異常。拋出的受檢異常都是對API用戶的一種潛在的指示:與異常相關(guān)的條件是調(diào)用這個方法的一種可能的結(jié)果。

有兩種未受檢的異常:運行時異常和錯誤。在行為上兩種是等同:它們都不需要捕獲。如果拋出的是未受檢異常或錯誤,往往就屬于不可恢復(fù)的情形,繼續(xù)執(zhí)行下去有害無益。如果程序未捕獲這樣的異常或錯誤,將會導(dǎo)致線程停止,并出現(xiàn)適當(dāng)?shù)腻e誤消息。

用運行時異常來表明編程錯誤。大多數(shù)的運行時異常都表示違返了API規(guī)約,API的客戶同有遵守API規(guī)范。例如,數(shù)組訪問的約定指明了數(shù)組的下標(biāo)值必須在零和數(shù)組長度減1之間,ArrayIndexOutOfBoundsException表明了這個規(guī)定。

按照慣例,錯誤往往被JVM保留用于表示資源不足、約束失敗,或者其他程序無法繼續(xù)執(zhí)行的條件。由于這已經(jīng)是個幾乎被普遍接受的慣例,因此最好不要再實現(xiàn)任何新的Error子類。因此,你實現(xiàn)的所有未受檢異常都應(yīng)該是RuntimeException的子類或間接是的。

總而言這,對于可恢復(fù)的情況,使用受檢的異常;對于程序錯誤,則使用運行時異常。當(dāng)然,這也不總是這么分明的。例如,考慮資源枯竭的情形,這可能是由于程序錯誤而引起的,比如分配了一塊不合理的過大的數(shù)組,也可能確實是由于資源不足而引起的。如果資源枯竭是由于臨時的短缺,或是臨時需求太大所造成的,這種情況可能就是可恢復(fù)的。API設(shè)計者需要判斷這樣的資源枯竭是否允許。如果你相信可允許恢復(fù),就使用受檢異常,否則使用運行時異常。如果不清楚,最好使用未受檢異常。

因為受檢異常往往指明了可恢復(fù)的條件,所以,這于這樣的異常,提供一些輔助方法尤其重要,通過這些方法,調(diào)用都可以獲得一些有助于恢復(fù)的信息。例如,假設(shè)因為沒有足夠的錢,他企圖在一個收費電話上呼叫就會失敗,于是拋出檢查異常。這個異常應(yīng)該提供一個訪問方法,以便用戶所缺的引用金額,從而可以將這個數(shù)組傳遞給電話用戶。

第59條:避免不必要地使用受檢異常

受檢異常與運行時異常不一樣,它們強迫程序員處理異常的條件,大大增強了可靠性,但過分使用受檢異常會使用API使用起來非常不方便。如果方法拋出一個或者多個受檢異常,調(diào)用都就必須在一個或多個catch塊中處理,或者將它們拋出并傳播出去。無論是哪種,都會給程序員添加不可忽視的負擔(dān)。

如果方法只拋出單個受檢異常,也會導(dǎo)致該方法不得在try塊中,在這種情況下,應(yīng)該問自己,是否有別的途徑來避免API調(diào)用者使用受檢的異常。這里提供這樣的參考,我們可以把拋出的單個異常的方法分成兩個方法,其中一個方法返回一個boolean,表明是否該拋出異常。這種API重構(gòu),把下面的調(diào)用:

try{//調(diào)用時檢查異常obj.action(args);//調(diào)用檢查異常方法 }catch(TheCheckedExcption e){// 處理異常條件... }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

重構(gòu)為:

if(obj.actionPermitted(args)){//使用狀態(tài)測試方法消除catchobj.action(args); }else{// 處理異常條件... }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

這種重構(gòu)并不總是合適的,但在合適的地方,它會使用API用起來更加舒服。雖然沒有前者漂亮,但更加靈活——如果程序員知道調(diào)用肯定會成功,或不介意由調(diào)用失敗而導(dǎo)致的線程終止,則下面為理為簡單的調(diào)用形式:

obj.action(args);
  • 1
  • 1

第60條:優(yōu)先使用標(biāo)準(zhǔn)異常

常見的可重用異常:

異常 使用時機
IllegalArgumentException 非null的參數(shù)值不正確
IllegalStateException 對象狀態(tài)不適合方法調(diào)用
NullPointerException 參數(shù)值是null,但這不允許
IndexOutOfBoundsException 索引參數(shù)值越界
ConcurrentModificationException 在禁止并發(fā)修改的情況下,檢測到對象的并發(fā)修改。
UnsupportedOperationException 對象不支持的方法

一定要確保拋出的異常的條件與該異常的文檔中的描述的條件是一致的,如果希望稍微增加更多的失敗-捕獲信息,可以把現(xiàn)有的異常進行子類化。

第61條:拋出與抽象對象相對應(yīng)的異常

如果方法拋出的異常與所執(zhí)行的任務(wù)沒有明顯的聯(lián)系,這種情形將會使人不知所措,當(dāng)?shù)讓拥漠惓鞑サ礁邔訒r往往會出現(xiàn)這種情況。這了使人困惑之外,拋出的底層異常類會污染高層的API(高層要依賴于底層異常類)。為了避免這個問題,高層在捕獲底層拋出的異常的同時,在捕獲的地方將底層的異常轉(zhuǎn)換后再重新拋出會更好:

// 異常轉(zhuǎn)換 try { // 調(diào)用底層方法 ... } catch(LowerLevelException e) {//捕獲底層拋出的異常后并轉(zhuǎn)換成適合自己系統(tǒng)的異常后再重新拋出 throw new HigherLevelException(...); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

下面是個來自AbstractSequentialList類中的底層異常轉(zhuǎn)換的實例,該數(shù)是List的一個抽象類,它的直接子類為LinkedList,在這個例子中,按照List接口中的get方法的規(guī)范(規(guī)范中說到:如果索引超出范圍 (index < 0 || index >= size()),就會拋出IndexOutOfBoundsException異常),底層方法只要可能拋出異常,我們就需要轉(zhuǎn)換這個異常,下面是AbstractSequentialList類庫的做法:

/** * Returns the element at the specified position in this list. * @throws IndexOutOfBoundsException if the index is out of range * ({@code index < 0 || index >= size()}). */ public E get(int index) { ListIterator<E> i = listIterator(index); try { return i.next();//Iterator的next會拋出NoSuchElementException運行時異常 } catch(NoSuchElementException e) { /* * 但接口規(guī)范是要求拋出IndexOutOfBoundsException異常,所以需要轉(zhuǎn)換。當(dāng)然這種 * 轉(zhuǎn)換也是合理的,因為該方法的功能特性就是按索引來取元素,在索引越界的情況 * 下拋出NoSuchElementException也是沒有太大的問題的(當(dāng)然劈開規(guī)范來說的),但 * 拋IndexOutOfBoundsException異常會更適合一些 */ throw new IndexOutOfBoundsException("Index: " + index); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

另一種異常轉(zhuǎn)換的形式是異常鏈,如果底層的異常對于高層調(diào)試有很大幫助時,使用異常鏈就非常合適,這樣在高層我們可以通過相應(yīng)的方法來獲取底層拋出的異常:

// 異常鏈 try { ... // 調(diào)用底層方法 } catch (LowerLevelException cause) {// 構(gòu)造異常鏈后重新拋出 throw new HigherLevelException(cause); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

盡管異常轉(zhuǎn)換與不加選擇地將捕獲到的底層異常傳播到高層中去相比有所改進,但是它不能濫用。處理來自底層異常的首選做法是根本就讓底層拋出異常,在調(diào)用底層方法前確保它會成功,從而來避免拋出異常,另外,我們有時也可以在調(diào)用底層方法前,在高層檢查一下參數(shù)的有效性,從而也可以避免異常的發(fā)生,當(dāng)然這種做法(不要拋出底層異常的做法)只是對底層拋出的是運行時異常時才可行。如果確實無法避免(如低層拋出的是受檢異常或是運行時異常但根本無法阻止)低層異常時,次選方案是讓高層繞開這些異常,并將異常使用日志記錄器記錄下來供事后調(diào)試。

總之,處理底層異常最好的方法首選是阻止底層異常的發(fā)生,如果不能阻止或者處理底層異常時,一般的做法是使用異常轉(zhuǎn)換(包括異常鏈轉(zhuǎn)換),除非底層方法碰巧可以保證拋出的異常對高層也合適才可以將底層異常直接從底層傳播到高層。異常鏈對高層和低層異常都提供了最佳的功能:它允許拋出適當(dāng)?shù)母邔赢惓5耐瑫r,又能捕獲底層的原因進行失敗分析。

第62條:每個方法拋出的異常都要有文檔描述

如果一個方法可能拋出多個異常類,則不要使用“快捷方式”聲明它會拋出這此異常類的某個超類。永遠不要聲明一個方法“throws Exception”,或者更糟的是聲明“throws Throwable”,這是極端的例子,因為它掩蓋了該方法可能拋出的其他異常。

對于方法可能拋出的未受檢異常,如果將這些異常信息很好地組織成列表文檔,就可以有效地描述出這個方法被成功執(zhí)行的前提條件。每個方法的文檔應(yīng)該描述它的前提條件,這是很重要的,在文檔中描述出未受檢的異常是滿中前提條件的最佳做法。

對于掊中的方法,在文檔中描述出它可能拋出的未受檢異常顯得尤其重要。這份文檔成了該接口的通用約定的一部分,它指定了該接口的多個實現(xiàn)必須遵循的公共行為。

未受檢異常也要在@throws標(biāo)簽中進行描述。

如果某類所有方法拋出同一個異常,那么這個異常的文檔可以描述在類文檔中。

總之,要為你編寫的每個方法所能擺好出的每個異常建立文檔,對于未受檢和受檢異常,以及對于抽象的和具體的方法也都一樣。

第63條:異常信息中要包含足夠詳細的異常細節(jié)消息

異常的細節(jié)消息對異常捕獲者非常有用,對異常的診斷是非常有幫助的。

為了捕獲失敗,異常的細節(jié)消息應(yīng)該包含所有“對該異常有作用”的參數(shù)和域值。例如IndexOutOfBoundsException異常的細節(jié)消息應(yīng)該包含下界、上界以及沒有落在界內(nèi)的下標(biāo)值,因為這三個值都有可能引起這個異常。

異常的細節(jié)消息不應(yīng)該與“用戶層次的錯誤消息”混為一談,后都對于最終用戶而言必須是可理解的。與用戶層次的錯誤消息不同,異常的詳細消息主要是讓程序員用來分析失敗原因的。因此,異常細節(jié)消息的內(nèi)容比可理解性重要得多。

為了確保在異常的細節(jié)消息中包含足夠的能捕獲失敗的信息,一種辦法是在異常的構(gòu)造器而不是字符串細節(jié)消息中引入這些信息。然后,有了這些信息,只要把它們放到消息描述中,就可以自動產(chǎn)生細節(jié)消息。例如,IndexOutOfBoundsException本應(yīng)該這樣設(shè)計的:

/** * Construct an IndexOutOfBoundsException. * * @param lowerBound the lowest legal index value. * @param upperBound the highest legal index value plus one. * @param index the actual index value. */ public IndexOutOfBoundsException(int lowerBound, int upperBound,int index) {// 構(gòu)建詳細的捕獲消息 super("Lower bound: " + lowerBound + ", Upper bound: " + upperBound + ", Index: " + index);// 存儲失敗的細節(jié)消息供程序訪問 this.lowerBound = lowerBound; this.upperBound = upperBound; this.index = index; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

但遺憾的是,Java平臺類庫并沒有使用這種做法,但是,這種做法仍然值得大力推薦。

第64條:努力使失敗保持原子性

當(dāng)一個對象拋出一個異常之后,我們總期望這個對象仍然保持在一種定義良好的可用狀態(tài)之中。對于被檢查的異常而言,這尤為重要,因為調(diào)用者通常期望從被檢查的異常中恢復(fù)過來。?
一般而言,一個失敗的方法調(diào)用應(yīng)該保持使對象保持在”它在被調(diào)用之前的狀態(tài)”。具有這種屬性的方法被稱為具有”失敗原子性(failure atomic)”。可以理解為,失敗了還保持著原子性。對象保持”失敗原子性”的方式有幾種:

  • 設(shè)計一個非可變對象。
  • 對于在可變對象上執(zhí)行操作的方法,獲得”失敗原子性”的最常見方法是,在執(zhí)行操作之前檢查參數(shù)的有效性。如下(Stack.java中的pop方法):
public Object pop() {if (size==0)throw new EmptyStackException();Object result = elements[--size];elements[size] = null;return result; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 與上一種方法類似,可以對計算處理過程調(diào)整順序,使得任何可能會失敗的計算部分都發(fā)生在對象狀態(tài)被修改之前。
  • 編寫一段恢復(fù)代碼,由它來解釋操作過程中發(fā)生的失敗,以及使對象回滾到操作開始之前的狀態(tài)上。
  • 在對象的一份臨時拷貝上執(zhí)行操作,當(dāng)操作完成之后再把臨時拷貝中的結(jié)果復(fù)制給原來的對象。

雖然”保持對象的失敗原子性”是期望目標(biāo),但它并不總是可以做得到。例如,如果多個線程企圖在沒有適當(dāng)?shù)耐綑C制的情況下,并發(fā)的訪問一個對象,那么該對象就有可能被留在不一致的狀態(tài)中。

即使在可以實現(xiàn)”失敗原子性”的場合,它也不是總被期望的。對于某些操作,它會顯著的增加開銷或者復(fù)雜性。?
總的規(guī)則是:作為方法規(guī)范的一部分,任何一個異常都不應(yīng)該改變對象調(diào)用該方法之前的狀態(tài),如果這條規(guī)則被違反,則API文檔中應(yīng)該清楚的指明對象將會處于什么樣的狀態(tài)。

第65條:不要忽略異常

當(dāng)一個API的設(shè)計者聲明一個方法會拋出某個異常的時候,他們正在試圖說明某些事情。所以,請不要忽略它!忽略異常的代碼如下:

try {... } catch (SomeException e) { }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

空的catch塊會使異常達不到應(yīng)有的目的,異常的目的是強迫你處理不正常的條件。忽略一個異常,就如同忽略一個火警信號一樣 – 若把火警信號器關(guān)閉了,那么當(dāng)真正的火災(zāi)發(fā)生時,就沒有人看到火警信號了。所以,catch塊至少應(yīng)該包含一條說明,用來解釋為什么忽略這個異常是合適的。

《Effective Java中文版 第2版》PDF版下載:?
http://download.csdn.net/detail/xunzaosiyecao/9745699

作者:jiankunking 出處:http://blog.csdn.net/jiankunking

from:?http://blog.csdn.net/jiankunking/article/details/54879618

總結(jié)

以上是生活随笔為你收集整理的Effective Java读书笔记五:异常的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。