Java異常系列第二集——捕獲所有異常

本博客為個人原創,轉載需在明顯位置注明出處

這篇文章我們來聊聊捕獲異常的內容,如果一個方法內部拋出兩個或多個異常,外部捕獲的順序是怎樣的?捕獲到的異常,我們怎樣打印出他的棧軌跡?如果捕獲到異常之后不作任何處理再次拋出,他的棧軌跡會改變嘛?在項目里面我們經常會看到xxx Exception causedBy xxx Exception,這個是怎么回事?下面我們就來詳細討論一下(本篇代碼較多)

異常匹配###

class CatchHelper {
    public void method(String arg1, String arg2) throws ParentException {
        if (arg1 == null) {
            throw new ChildException();
        }
         if (arg2 == null) {
            throw new ParentException();
         }
    }
}

@Test
public void testCatchException() {
    try {
        String arg1 = null;
        String arg2 = "";
         CatchHelper helper = new CatchHelper();
         helper.method(arg1, arg2);
    } catch (ChildException e) {
        System.out.println("Catch ChildException");
    } catch (ParentException e) {
        System.out.println("Catch ParentException");
    } catch (Exception e) {
        System.out.println("Catch Exception");
     }
}

代碼中有兩個自定義異常,從名稱就可以明白,ChildException是繼承自ParentException。再看代碼,method方法中對兩個參數判空,分別拋出這兩個異常,按代碼中的異常捕獲,可以正常捕獲兩個異常。但是,如果將catch代碼塊中的ChildException和ParentException交換順序,在ChildException編譯器就會報錯,提示該異常時unreachable的。因為異常捕獲的順序是從上往下的,如果上面的異常匹配了,下面的異常就沒用了,代碼中ChildException是ParentException的子類,若ParentException放在catch代碼塊的第一位,無論是Child還是Parent都會被捕獲,排在第二位的ChildException就是無用的。總結成一句話就是:異常捕獲要從上到下,從小到大

棧軌跡###

打印異常棧軌跡很簡單,就是e.printStackTrace()方法的調用,打印出的是從異常拋出點的方法名到系統最底層的方法棧信息,不多解釋,沒有試過的可以自己去試下

重新拋出異常###

class RethrowHelper {

     public void methodA() throws MyException {
         System.out.println("this is methodA()");
         throw new MyException("thrown from methodA()");
     }

     public void methodB() throws MyException {
         try {
             methodA();
         } catch (MyException e) {
            System.out.println("Catch MyException in catch block from methodB()");      
            // e.printStackTrace();
            // 直接拋出捕獲到的異常
            throw e;
         }
     }

     public void methodC() throws MyException {
         try {
             methodA();
         } catch (MyException e) {
             System.out.println("Catch MyException in catch block from methodC()");
             // e.printStackTrace();
             // 替換捕獲到的異常的棧路徑,然后再拋出
             e.fillInStackTrace();
             throw e;
         }
     }
}

@Test
public void testRethrow() {
    RethrowHelper helper = new RethrowHelper();
    try {
        helper.methodB();
     } catch (MyException e) {
         System.out.println("Main printStackTrace");
         e.printStackTrace();
     }
     
     try {
         helper.methodC();
     } catch (MyException e) {
         System.out.println("Main printStackTrace");
         e.printStackTrace();
     }
}

從代碼中可以看出,methodB方法捕獲到異常之后直接拋出,methodC方法捕獲到methodA的異常之后,調用了e.fillInStackTrace()方法,然后再拋出。區別在于,如果直接拋出,異常的棧信息還是保持不變,也就是說testRethrow測試方法中打印出的methodB的異常信息仍然是從methodA拋出的。若像methodC一樣,調用了e.fillInStackTrace()方法再拋出,異常就會先把當前方法作為棧頂信息,那么在外部testRethrow測試方法中捕獲到的棧信息就是從methodC拋出的。光文字解釋看起來可能有點費解,直接看運行結果:

Paste_Image.png

testRethrow測試方法捕獲到的methodB的異常信息還是從methodA拋出的,異常信息沒變

Paste_Image.png

methodC的異常信息就是從methodC拋出的,之前methodA的信息不存在了,看出區別了吧?

異常鏈###

class CauseHelper {
    public void setValue(String value) throws ValueValidException {
        if (value == null) {
            ValueInvalidException exception = new ValueInvalidException();
              // 將空指針異常設置為ValueInvalidException的起因,即CausedBy ......
              exception.initCause(new NullPointerException());
              throw exception;
         }
     }
}

@Test
public void testCause() {
     CauseHelper helper = new CauseHelper();
     try {
         helper.setValue(null);
     } catch (ValueValidException e) {
         e.printStackTrace();
     }
}

在開發過程中,我們經常會看到logcat上打印出的日志有兩個異常信息,中間由causedBy連接,如上述代碼的運行結果所示:

Paste_Image.png

setValue方法中判斷value如果為空,就拋出一個自定義的ValueInvalidException,接著調用了異常對象的initCause方法,傳入了一個NullPointerException作為參數,不難理解,這是給拋出的異常添加一個因果關系,說明我拋出的ValueInvalidException是由NullPointerException導致的,這樣的異常信息更加專業全面。

關于捕獲異常就聊到這里,下一篇要分享的是運行時多態中的異常,歡迎拍磚!

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容