EffectiveJava第9章-異常

第57條:只針對(duì)異常的情況處理異常

try{
    int i = 0;
    while(true)
        range[i++].climb(); 
}catch(ArrayIndexOutOfBoundsException e){
}

用拋出(throw)、捕獲(catch)亏较、忽略ArrayIndexOutOfBoundsException的手段來(lái)達(dá)到終止無(wú)限循環(huán)的目的雪情。這是極其不合適的。原因有:

1.異常機(jī)制的設(shè)計(jì)初衷是用于不正常的情形尘执,所以很少會(huì)有JVM實(shí)現(xiàn)試圖對(duì)它們進(jìn)行優(yōu)化宴凉,使得與顯式的測(cè)試一樣快速。
2.把代碼放在try-catch塊中反而阻止了現(xiàn)代JVM實(shí)現(xiàn)本來(lái)可能要執(zhí)行的某些特定優(yōu)化丧靡。
3.對(duì)數(shù)組進(jìn)行遍歷的標(biāo)準(zhǔn)模式并不會(huì)導(dǎo)致冗余的檢查籽暇。有些現(xiàn)代的JVM實(shí)現(xiàn)會(huì)將它們優(yōu)化掉。

基于異常的循環(huán)模式不僅模糊了代碼的意圖熬荆,降低了它的性能绸狐,而且它還不能保證正常的工作!可能掩蓋Bug(捕捉到了異常突琳,但不做任何處理)!

這條原則對(duì)于API設(shè)計(jì)也有啟發(fā)拆座。設(shè)計(jì)良好的API不應(yīng)該強(qiáng)迫它的客戶(hù)端為了正常的控制流而使用異常冠息。

1.如果類(lèi)具有“狀態(tài)相關(guān)”的方法逛艰,往往也應(yīng)該有“狀態(tài)測(cè)試”的方法。比如散怖,Iterator接口有一個(gè)“狀態(tài)相關(guān)”的next方法镇眷,和相應(yīng)的狀態(tài)測(cè)試方法hasNext。所以對(duì)集合進(jìn)行迭代的標(biāo)準(zhǔn)模式:

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

而不是通過(guò)異常捕獲的方式結(jié)束遍歷永乌。

2.如果狀態(tài)相關(guān)的方法被調(diào)用時(shí)具伍,該對(duì)象處于不適當(dāng)?shù)臓顟B(tài)之中,它就會(huì)返回一個(gè)可識(shí)別的值望几,比如null萤厅。但這個(gè)并不適合Iterator,因?yàn)閚ull也是next的合法返回值害碾。

第58條:對(duì)可恢復(fù)的情況使用受檢異常赦拘,對(duì)編程錯(cuò)誤使用運(yùn)行時(shí)異常

Java程序設(shè)計(jì)提供三種可拋出的異常躺同,受檢異常、運(yùn)行時(shí)異常和錯(cuò)誤剃袍。(運(yùn)行時(shí)異常和錯(cuò)誤又稱(chēng)為非受檢異常)捎谨。

受檢異常應(yīng)該被捕獲,非受檢異常不應(yīng)該被捕獲畏邢。

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

受檢異常強(qiáng)迫程序員處理異常的條件检吆,大大增強(qiáng)了可靠性。過(guò)分使用受檢的異常會(huì)使API使用起來(lái)非常不方便臂寝。

如果正確地使用API并不能阻止這種異常條件的產(chǎn)生摊灭,
并且一旦產(chǎn)生異常帚呼,使用API的程序員可以立即采取有用的動(dòng)作(恢復(fù)),
除非上述兩個(gè)條件都成立萝挤,否則更適合使用未受檢的異常怜珍。

把受檢異常變?yōu)槲词軝z異常:
把拋出異常的方法分成兩個(gè)方法,其中第一個(gè)方法返回一個(gè)boolean今豆,表明是否應(yīng)該拋出異常柔袁。

try{
  obj.action(args);
}catch(TheCheckedException e){
  //Handle exceptional condition
  ...
}
重構(gòu)為
if(obj.actionPermitted(args)){
  obj.action(args);
}else{
  //Handle exception condition
  ...
}

如果明知道會(huì)調(diào)用成功,或者不介意由于調(diào)用失敗而導(dǎo)致的線(xiàn)程終止插掂,可以使用:
 obj.action(args);

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

常見(jiàn)的異常

異常 使用場(chǎng)合
NullPointerException 在禁止使用null的情況下,對(duì)象為null
IndexOutOfBoundsException 下表參數(shù)值越界
IllegalArgumentException 非null的參數(shù)值不正確
IllegalStateException 對(duì)于方法調(diào)用而言酝润,對(duì)象狀態(tài)不合適

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

當(dāng)方法傳遞 由低層抽象拋出的異常時(shí)要销,往往會(huì)出現(xiàn)方法拋出的異常與它所執(zhí)行的任務(wù)沒(méi)有明顯的聯(lián)系夏块。

因此,高層的實(shí)現(xiàn)應(yīng)該捕獲低層的異常浑塞,同時(shí)拋出可以按照高層抽象進(jìn)行解釋的異常患民。這叫“異常轉(zhuǎn)譯”

try{
    //Use lower-level abstraction to do our bidding
    ...
}catch(LowerLevelException e){
    throw new HigherLevelException(...);
}

例如AbstractSequentialList類(lèi):

    /**
     * Returns the element at the specified position in this list.
     *
     * <p>This implementation first gets a list iterator pointing to the
     * indexed element (with <tt>listIterator(index)</tt>).  Then, it gets
     * the element using <tt>ListIterator.next</tt> and returns it.
     *
     * @throws IndexOutOfBoundsException {@inheritDoc}
     */
    public E get(int index) {
        try {
            return listIterator(index).next();
        } catch (NoSuchElementException exc) {
            throw new IndexOutOfBoundsException("Index: "+index);
        }
    }

第62條:每個(gè)方法拋出的異常都要有文檔

描述一個(gè)方法所拋出的異常,是正確使用這個(gè)方法時(shí)所需要文檔的重要組成部分印蓖。

始終要單獨(dú)地聲明受檢的異常,并且利用Javadoc的@throws標(biāo)記赦肃,準(zhǔn)確地記錄下拋出每個(gè)異常的條件。明確的聲明所有拋出的異常船侧。

對(duì)于可能拋出的非受檢異常厅各,也要為它們建立文檔队塘,這樣可以有效地描述出這個(gè)方法被成功執(zhí)行的前提條件

//ConcurrentHashMap的get方法憔古,key不能為空值鸿市。
     /**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     * ......
     *
     * @throws NullPointerException if the specified key is null
     */
    public V get(Object key) {
        ......
    }

在接口中的方法即碗,在文檔中記錄下它可能拋出的未受檢異常顯得尤為重要涝桅。這份文檔成了該接口的通用約定冯遂。比如ConcurrentMap接口的putIfAbsent()方法可能拋出NullPointerException谒获,文檔中約定了任何繼承了ConcurrentMap的類(lèi)的key和value都不能為null。

public interface ConcurrentMap<K, V> extends Map<K, V> {
   /**
    * If the specified key is not already associated
    * with a value, associate it with the given value.
    * This is equivalent to
    * <pre>
    *   if (!map.containsKey(key))
    *       return map.put(key, value);
    *   else
    *       return map.get(key);</pre>
    * except that the action is performed atomically.
    *
    * @param key key with which the specified value is to be associated
    * @param value value to be associated with the specified key
    * @return the previous value associated with the specified key, or
    *         <tt>null</tt> if there was no mapping for the key.
    *         (A <tt>null</tt> return can also indicate that the map
    *         previously associated <tt>null</tt> with the key,
    *         if the implementation supports null values.)
    * @throws UnsupportedOperationException if the <tt>put</tt> operation
    *         is not supported by this map
    * @throws ClassCastException if the class of the specified key or value
    *         prevents it from being stored in this map
    * @throws NullPointerException if the specified key or value is null,
    *         and this map does not permit null keys or values
    * @throws IllegalArgumentException if some property of the specified key
    *         or value prevents it from being stored in this map
    *
    */
   V putIfAbsent(K key, V value);
   
   ......
}

使用Javadoc的@throws標(biāo)簽記錄下一個(gè)方法可能拋出的每個(gè)未受檢異常,但是不要使用throws關(guān)鍵字將未受檢的異常包含在方法的聲明中炒俱。
throws關(guān)鍵字只將受檢的異常包含在方法的聲明中爪膊。這樣有Javadoc的@throws標(biāo)簽所產(chǎn)生的文檔就會(huì)有明顯的提示信息來(lái)幫助區(qū)分受檢異常和非受檢異常。

總結(jié):使用Javadoc的@throws標(biāo)簽將方法可能拋出的所有異常都要明確地記錄下來(lái)峦阁,而在方法聲明的throws關(guān)鍵字耘成,只記錄受檢異常瘪菌。這樣程序員就可以明確地區(qū)分方法所拋出來(lái)的受檢異常和非受檢異常。

第63條:在細(xì)節(jié)消息中包含能捕獲失敗的信息

為了捕獲失敗诵肛,異常的細(xì)節(jié)信息應(yīng)該包含所有“對(duì)異常有貢獻(xiàn)”的參數(shù)和域的值疆栏。

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

一般而言,失敗的方法調(diào)用應(yīng)該使對(duì)象保持在被調(diào)用之前的狀態(tài)珠洗。具有這種屬性的方法被稱(chēng)為具有失敗原子性若专。

有幾種途徑可以實(shí)現(xiàn)這種效果:

1.設(shè)計(jì)一個(gè)不可變對(duì)象;
2.在執(zhí)行操作之前檢查參數(shù)的有效性膊爪;

public Object pop(){
    if(size == 0)
        throw new EmptyStackException();
    Object result = elememts[--size];
    elements[size] = null; //銷(xiāo)除廢棄的引用
    return result;
}

如果不對(duì)size進(jìn)行檢查米酬,這個(gè)方法仍會(huì)拋出異常,但是加派,size的值保持在不一致的狀態(tài)(-1)之中跳芳。

3.寫(xiě)一段恢復(fù)代碼,回滾
4.在對(duì)象的一份臨時(shí)拷貝上執(zhí)行操作娄琉,當(dāng)操作完成之后再用臨時(shí)拷貝中的結(jié)果代替對(duì)象的內(nèi)容吓歇。比如照瘾,Collections.sort在執(zhí)行排序之前,首先把輸入列表轉(zhuǎn)到了一個(gè)數(shù)組中主卫。

第65條:不要忽略異常

try{
  ...
}catch(SomeException e){
}

空的catch塊會(huì)使異常達(dá)不到應(yīng)有的目的鹃愤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末软吐,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子姿现,更是在濱河造成了極大的恐慌肖抱,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件提佣,死亡現(xiàn)場(chǎng)離奇詭異拌屏,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)每篷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,347評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)务唐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)枫笛,“玉大人刚照,你說(shuō)我怎么就攤上這事∥夼希” “怎么了浑彰?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,435評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)颜价。 經(jīng)常有香客問(wèn)我周伦,道長(zhǎng)未荒,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,509評(píng)論 1 284
  • 正文 為了忘掉前任寨腔,我火速辦了婚禮率寡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘勇劣。我一直安慰自己靖避,他們只是感情好潭枣,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,611評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著幻捏,像睡著了一般盆犁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上篡九,一...
    開(kāi)封第一講書(shū)人閱讀 49,837評(píng)論 1 290
  • 那天谐岁,我揣著相機(jī)與錄音,去河邊找鬼榛臼。 笑死伊佃,一個(gè)胖子當(dāng)著我的面吹牛沛善,可吹牛的內(nèi)容都是我干的航揉。 我是一名探鬼主播,決...
    沈念sama閱讀 38,987評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼金刁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼帅涂!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起尤蛮,我...
    開(kāi)封第一講書(shū)人閱讀 37,730評(píng)論 0 267
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤媳友,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后产捞,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體醇锚,經(jīng)...
    沈念sama閱讀 44,194評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,525評(píng)論 2 327
  • 正文 我和宋清朗相戀三年坯临,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了焊唬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,664評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡尿扯,死狀恐怖求晶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情衷笋,我是刑警寧澤芳杏,帶...
    沈念sama閱讀 34,334評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站辟宗,受9級(jí)特大地震影響爵赵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜泊脐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,944評(píng)論 3 313
  • 文/蒙蒙 一空幻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧容客,春花似錦秕铛、人聲如沸约郁。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,764評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鬓梅。三九已至,卻和暖如春谨湘,著一層夾襖步出監(jiān)牢的瞬間绽快,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,997評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工紧阔, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坊罢,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,389評(píng)論 2 360
  • 正文 我出身青樓擅耽,卻偏偏與公主長(zhǎng)得像活孩,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乖仇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,554評(píng)論 2 349

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