Java異常最佳實踐筆記

Java異常最佳實踐筆記

引用

《如何善用Java異常》
https://juejin.im/post/5bacd8975188255c69780e7b

筆記

  • Java中的異常分爲兩種:
    1. 無法捕獲處理的系統級別Error.
    2. 可以被認爲處理的檢測異常Exception.
    • 重點是他們都繼承了Throwable接口
  • 其中需要我們處理的只有Exception的子類。Exception的子類異常都是【可以】被我們的捕獲處理的。可以被我們處理的意思,即可不處理,也可以處理。(即,可以處理,但沒必要)
  • 其中Exception的子類RuntimeException則允許我們不對他處理,它是一種特殊Exception,特殊之處在於允許我們不對它做捕獲處理, 這種異常叫運行時異常,意思就是運行期間纔會產生的,理論上大部分異常都是在運行期間纔會產生了。也就是說大部分異常我們都可以置之不理,因爲他是運行期間纔會產生的,運行期間發現的意外大部分都是我們無法考慮到的(就像我們的生活,“意外”總比“預料”多。)
  • 異常最佳實踐:
    • 能用runtimeExcepiton,用runtimeException。把異常拋給運行時處理,畢竟大部分的異常都是無法認爲處理的,直接tryCatch也無法處理的異常,只能拋給運行時了。
    • 能tryCatch的異常就不算異常,因爲既然能tryCatch證明你能處理它,比如IOException,你TryCatch了這個IO異常,就表示有默認值返回(比如Null),如果無法返回默認值,請tryCatch後拋出運行時異常。
    • 關於異常的日誌問題,無論什麼異常處理最好都logger打印一下日誌,並且寫明原因,如果有必要最好,Logger.info(“錯誤信息”,ex); 或者 log.XXX(Object obj,Thowable e) 的形式打印一下日誌,這樣就可以順帶打印出exceptionStackTrace.
    • 其次就是不要使用ex.printStacktrace(),因爲這是不好的習慣,請用log.XXX(Object obj,Thowable e) 的形式的代替他
      • 理由:1. 你很難確定它會打印到哪個文件,讓logger框架管理比較好。2.它有可能打印給終端用戶頁面一堆亂七八糟的信息。
    • 定義一個全局的異常捕捉器(spring的全局異常捕捉器ErrorController),對所有錯誤異常進行解析處理成頁面返回給終端用戶頁面。具體怎麼實現呢?查一下《spring全局異常處理》相關資料即可。

關於異常比較好的知識點:

在這裏插入圖片描述

  • Java7與異常Java7對異常做了兩個改進。第一個是try-with-resources,第二個是catch多個異常。
  • try-with-resources主要是針對IOException基本都是需要關閉資源的,所以用try-with-resources來關閉資源,可以讓代碼更好看。

不使用try-with-resources,我們在使用io等資源對象時,通常是這樣寫的:String getReadLine() throws IOException {
    BufferedReader br = new BufferedReader(fileReader);
    try {
        return br.readLine();
    } finally {
        if (br != null) br.close();
    }
}
複製代碼使用try-with-recources的寫法:String getReadLine() throws IOException {
    try (BufferedReader br = new BufferedReader(fileReader)) {
        return br.readLine();
    }
}

顯然,編繹器自動在try-with-resources後面增加了判斷對象是否爲null,如果不爲null,則調用close()函數的的字節碼。

只有實現了java.lang.AutoCloseable接口,或者java.io.Closable(實際上繼隨自java.lang.AutoCloseable)接口的對象,纔會自動調用其close()函數。

有點不同的是java.io.Closable要求一實現者保證close函數可以被重複調用。而AutoCloseable的close()函數則不要求是冪等的。
具體可以參考Javadoc。

但是,需要注意的是try-with-resources會出現異常覆蓋的問題,也就是說catch塊拋出的異常可能會被調用close()方法時拋出的異常覆蓋掉。我們會在下面的小節講到異常覆蓋。

多異常捕捉直接上代碼:public static void main(String[] args) {
    try {
        int a = Integer.parseInt(args[0]);
        int b = Integer.parseInt(args[1]);
        int c = a / b;
        System.out.println("result is:" + c);
    } catch (IndexOutOfBoundsException | NumberFormatException | ArithmeticException ie) {
        System.out.println("發生了以上三個異常之一。");
        ie.getMessage();
        // 捕捉多異常時,異常變量默認有final修飾,
        // 所以下面代碼有錯:
        // ie = new ArithmeticException("test");
    }
}

作者:xy的技術圈
鏈接:https://juejin.im/post/5bacd8975188255c69780e7b
來源:掘金
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
什麼是異常覆蓋正如我們前面提到的,在finally塊調用資源的close()方法時,是有可能拋出異常的。與此同時我們可能在catch塊拋出了另一個異常。那麼catch塊拋出的異常就會被finally塊的異常“吃掉”。看看這段代碼,調用test()方法會輸出什麼?void test() {
    try {
        overrideException();
    } catch (Exception e) {
        System.out.println(e.getMessage());
    }
}

void overrideException() throws Exception {
    try {
        throw new Exception("A");
    } catch (Exception e) {
        throw new Exception("B");
    } finally {
        throw new Exception("C");
    }
}
複製代碼會輸出C。可以看到,在catch塊的B被吃掉了。JDK提供了Suppressed的兩個方法來解決這個問題:// 調用test會輸出:// C// Avoid test() {
    try {
        overrideException();
    } catch (Exception e) {
        System.out.println(e.getMessage());
        Arrays.stream(e.getSuppressed())
                .map(Throwable::getMessage)
                .forEach(System.out::println);
    }
}

void overrideException() throws Exception {
    Exception catchException = null;
    try {
        throw new Exception("A");
    } catch (Exception e) {
        catchException = e;
    } finally {
        Exception exception = new Exception("C");
        exception.addSuppressed(catchException);
        throw exception;
    }
}
複製代碼異常鏈你可以在拋出一個新異常的時候,使用initCause方法,指出這個異常是由哪個異常導致的,最終形成一條異常鏈。詳情請查閱公衆號之前的關於異常鏈的文章。

作者:xy的技術圈
鏈接:https://juejin.im/post/5bacd8975188255c69780e7b
來源:掘金
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章