Android基礎進階之EffectiveJava翻譯系列(第八章:異

ldzsl發表於2021-09-09

高效使用異常指南

Item 57: Use exceptions only for exceptional conditions

只有在異常條件下才使用異常

考慮如下的程式碼

//bad !! don't do thistry {    int i = 0;    while(true)
        range[i++].climb();
} catch(ArrayIndexOutOfBoundsException e){}

使用異常條件來終止遍歷操作是非常錯誤的做法

使用如下程式碼代替

for (Mountain m : range)
    m.climb();

名副其實,異常只能用在異常條件下,而不能用於流程控制


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

對可恢復條件使用檢查異常,對程式錯誤使用執行時異常。

Java提供了三種異常:checked exceptions,runtime exceptions and errors.大多數程式設計師對何時使用何種異常有困惑,有如下幾種原則作參考

決定使用檢查異常或非檢查異常的原則是:呼叫方可以合理的修復異常,現如今的IDE工具如eclipse或Android studio會自動提示此類異常,自動填充try catch

非檢查異常有兩種:runtime exceptions and errors

用執行時異常(runtime exceptions)識程式錯誤,絕大多數的執行時異常都表明違反了某種前提條件,如ArrayIn�dexOutOfBoundsException陣列越界

有一個普遍的約定是error用於JVM,所以所有的非檢查異常都應該繼承自 RuntimeException

常見的非檢查異常runtime exception

  • NullPointerException, 空指標異常

  • ArithmeticException, 算術異常

  • ClassCastException, 型別強制轉換異常

  • IllegalArgumentException, 傳遞非法引數異常


Item 59: Avoid unnecessary use of checked exceptions

避免過度使用檢查異常
如果一個方法有多個檢查異常,呼叫者會包裹多個catch來處理異常,這裡沒有一個絕對的準則,可以使用if語句把條件先過濾一遍而避免拋異常


Item 60: Favor the use of standard exceptions

使用常用異常
當需要丟擲一個異常時,儘量使用Java平臺提供好的異常,因為大家都知道什麼意思


Item 61: Throw exceptions appropriate to the abstraction

丟擲更合適的抽象異常

當丟擲和任務不相關的異常時容易讓人困惑,當丟擲一個低階的異常時,這種情況經常發生,它不僅僅令人困惑,也汙染了上層的呼叫

為了避免這個問題,上層應該捕獲較低階別的異常,並在它們的位置上丟擲可以用更高階別的抽象來解釋的異常,如:

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

更具體的例子在List<E>的方法中

/**
* 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();
    } catch(NoSuchElementException e) {        throw new IndexOutOfBoundsException("Index: " + index);
    }
}

這種特殊的異常處理方式被稱為"異常鏈",但是也不應該過度使用

我們也可以在在上層呼叫中丟擲底層錯誤的原因

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

最好的方式還是儘量在底層避免異常,如果不能處理在考慮"異常鏈"的方式


Item 62: Document all exceptions thrown by each method

為每個方法丟擲的異常新增文件註釋

對於檢查異常,要說明前置條件,並用@throws標記合適的異常,不要為了圖簡單直接 @throws Exception

如果一個異常被多個方法丟擲,並且是相同的原因,則不要在方法上註釋,而是要在類上新增異常註釋


Item 63: Include failure-capture information in detail messages

列印出異常的詳細資訊以便於分析


Item 64: Strive for failure atomicity

保證錯誤的原子性(呼叫一千次,輸出一樣)

即使一個異常發生了,我們也希望物件能正常使用

有幾種方式可以達到這一點,最簡單的就是建立不可變的物件,如果一個物件是不可變的,原子性也隨之而來

對於可變物件,常見的方式是檢查引數的合法性(Item 38)

第三種方式是發生異常後,回滾異常狀態為使用前的初始狀態

最後一種方式使用複製來避免發生錯誤時改變原來物件的狀態,如Collections.sort,排序方法會先轉成array陣列來排序,本來是為了提高效能,額外的如果發生了錯誤不會改變原集合的狀態

雖然原子效能保證,但是實際使用中並不總是能達到滿意的狀態,如兩個執行緒同時修改一個物件,沒有同步的情況下,引發了currentModificationException.這時如果恢復物件的狀態依然不能使程式正確執行

作為一種規則來講,當發生異常時總能確保當前物件的狀態,可惜目前很多API都沒有遵守


Item 65: Don’t ignore exceptions

不要忽略異常

這條建議看似很明顯,但是值得重複,當API的設計者宣告瞭一種異常,他們是為了告訴你什麼,不要忽略它!很容易包裹一個空的try catch語句就不管這個異常了.如:

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

空處理並不是拋異常的初衷,目的是為了強制解決這個異常條件.忽略異常就像忽略火警廣播,有人關了廣播導致其他人並不知道發生了火災.如果確實需要一個空的處理,需要詳細說明為何空處理是合適的



作者:青樓愛小生
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2325/viewspace-2821962/,如需轉載,請註明出處,否則將追究法律責任。

相關文章