1. 程式人生 > >java基礎(十)捕獲異常還是丟擲異常

java基礎(十)捕獲異常還是丟擲異常

1、 異常分類

a. RuntimeException 及其子類不要求捕捉,而其它的異常要求捕捉隨便舉幾個 RuntimeException 子異常,有:陣列越界異常、空指標異常、0作除數異常

b. 非RuntimeException 異常有:Socket異常、IO異常等

c. 對比一下我們就會發現,RuntimeException 是在程式中可以完全避免的,
c1. 比如陣列越界異常,只要我在程式裡作個判斷,如果要訪問的陣列元素下標和陣列的長度作一下比較就知道會不會越界,
c2. 再比如空指標異常,如果在訪問物件時判斷一下物件的變數是否為空就可以了。
c3. 而非RuntimeException 則是程式無法避免的,比如IO異常,你的程式正在讀一個檔案,而這個檔案所在磁碟出現了壞道,這就必然會引發IOException,這是不是靠程式設計高手編寫完美的程式就可以法避免得了的,程式所能做的只有出現異常之後怎麼處理的問題

2、 異常處理原則之一:延遲捕獲

意思是,當異常發生時,不應立即捕獲,而是應該考慮當前作用域是否有有能力處理這一異常的能力,如果沒有,則應將該異常繼續向上丟擲,交由更上層的作用域來處理。

一個例子:某方法String readFile(String filename),會去嘗試讀出指定檔案的內容並返回,其使用FileInputStream來讀取指定檔案,而FileInputStream的構造方法會丟擲FileNotFoundException,這是一個Checked Exception。那麼readFile方法是應該捕獲這個異常,還是丟擲這個異常呢?很顯然應該丟擲。因為readFile這個方法可能會在不同的場景下,被不同的程式碼呼叫,在這些場景中,出現“檔案未找到”的情況時的處理邏輯可能是不同的,例如某場景下要發出告警資訊,另一場景下可能會嘗試從另一個檔案中讀取,第三個場景下可能需要將錯誤資訊提示給使用者。在這種情況下,在readFile方法內的作用域中,是處理不了這個異常的,需要丟擲,交由上層的,具備了處理這個異常的能力的作用域來處理

在伺服器端和dao段 都是丟擲異常很少有捕獲 捕獲在客戶端 因為不能吧異常拋給使用者。從dao到伺服器到客戶端都是層層丟擲 到客戶端必須捕獲處理 可能給使用者提示資訊和給程式猿一些記錄 方便互動和處理

3、 個人經驗

a. 以MVC框架為例,首先controller層必須捕獲異常一般情況下不允許將系統內部的異常不做任何封裝處理直接拋給客戶端,這樣對系統來說會暴露過多資訊(異常棧資訊都拋給客戶端,可能會把SQL結構都丟擲去),這是不安全因素;同時,這對使用者來說也是不友好的,讓一些不搞計算機的同學看得一頭霧水,並且懷疑網站的正規性與可靠性,可能會直接影響未來的業務。因此,通常需要在controller層封裝一些錯誤碼、使用者友好語句(系統維護中等等欺騙使用者的語句)。當然這也不是鐵律,有的時候需要後臺進行較檢,可能需要丟擲一些非公用的異常資訊到介面上,比如之前車貸專案中用到過的上傳附件檢查,就是用後臺異常提示使用者的。還有就是傳入引數有效性較檢,也需要丟擲自定義異常(用@NotNull或NotEmpty等註解丟擲message資訊),即不需要捕獲後統一處理返回到使用者介面

b. 接著說一下service層,業務層需要處理業務邏輯,當然這一層不需要考慮引數是否為空,需要考慮的僅僅是從dao查詢相關的異常,比如ConnectionExcepion、SQLException、BadSQLGrammerException等異常,這些異常我通常會去捕獲,因為這些異常涉及到業務邏輯是否能正常執行,確實是service該考慮的事。做法是try…catch…捕獲所有Exception,然後在catch中用logger.error(“message, e = {}”, e.getMessage())記錄日誌資訊,用於到時候定位error程式碼位置,然後根據實際業務情況,看這個異常捕獲後是否需要用一個異常封裝一下資訊拋給控制層,然後由控制層統一處理也好或者直接返回給使用者)。注意,業務層異常不是用於捕獲NullPointerException等無聊的程式碼健壯性問題,這些都是業務層程式碼邏輯沒控制好,查詢值需要做一下非空判斷

c. 至於dao層,我想沒必要說了吧,一般我也不寫實現類,持久層的mybatis3.X都用動態代理來實現了。 BTW,try…catch最好有針對性,不要把所有美容都放到try裡面檢查是否有異常,一般來說總有些無關異常的語句可以提到異常塊之外,反正能提就提。

4、 異常總結

異常:

1、 自定義異常
2、 重寫方法需要丟擲與原方法所丟擲異常型別一致異常或不丟擲異常
3、 異常型別圖:
Throwable -> Error(虛擬機器錯誤)
Exception -> RuntimeException(可以丟擲也可以不丟擲)
4、 先捕獲小的,再捕獲大的

public class Test {
    public void regist(int num) throws MyException {
        if (num < 0) 
            throw new MyException("人數為負值,不合理", 10000);
        System.out.println("登記人數:" + num);
    }

    public void manager() {
        try {
            regist(-10);
        } catch(MyException e) {
            System.out.println("登記失敗,出錯型別碼: " + e.getId());
            e.printStackTrace();
        }
        System.out.println("操作結束");
    }
    public static void main(String[] args) {
        Test t = new Test();
        t.manager();
    }

}

class MyException extends Exception {
    private int id; // 異常碼
    public MyException(String message, int id) {
        super(message);
        this.id = id;
    }

    public int getId() {
        return this.id;
    }
}