異常體系:
? ? ? ? (1)所有異常(Exception)、錯誤(Error)都繼承自異常中的基類:Throwable。而異常又可以分為檢查異常(Checked Exception)、非檢查異常(Unchecked Exception)兩大類。
? ? ? ? (2)檢查異常:在編譯期間由編譯器檢查的異常,編譯器確保這些異常在編譯期被處理,意味著不能直接使用關鍵字throw拋出異常,要么使用try、catch處理異常,要么在方法聲明上使用throws關鍵字提醒方法調用者該方法可能會拋出的異常,讓方法調用者處理異常。Exception從屬子類中,除了RuntimeException類及其從屬子類,其它子類都屬于這一類型的異常。
? ? ? ? 直接拋出檢查異常:
? ? ? ? 編譯器直接報錯,提示該異常需要處理。
? ? ? ? 使用try、catch捕獲異常:
? ? ? ? 編譯器不會報錯,因為在catch塊捕獲了異常,并進行了處理(e.printStackTrace())。
? ? ? ? 在方法簽名上拋出異常:
? ? ? ? 編譯器不會報錯,因為在調用該方法時,編譯器會強制要求方法調用者使用try、catch捕獲異常進行處理,或者繼續通過throws關鍵字往上拋,讓更上一層的方法調用者進行處理。
? ? ? ? (3)非檢查異常:編譯器不會在編譯期間就檢查這類異常,直接拋出這類異常編譯器不會報錯,只有在程序運行時才可能拋出的異常。RuntimeException及其從屬子類、Error及其從屬子類都屬于這類異常。
? ? ? ? 直接拋出非檢查異常:
? ? ? ? 編譯器不會報錯,因為非檢查異常只會在程序運行時才會進行相應處理。
? ? ? ? (4)如果想自定義檢查異常,那么讓類直接繼承Exception類即可;如果想自定義非檢查異常,那么讓類直接繼承自RuntimeException即可。
? ? ? ? 自定義檢查異常:
? ? ? ? 自定義非檢查異常:
異常處理機制:
? ? ? ? (1)try-catch字節碼解析
? ? ? ?JavaCodes:
public class TestMyException{public static void main(String[] args) {System.out.println("使用try、catch捕獲自定義檢查異常");try {throw new MyException();} catch (MyException e) {//將自定義檢查異常封裝成非檢查異常throw new RuntimeException(e);}}
}
class MyException extends Exception{}
? ? ? ? ByteCodes:
Code:stack=3, locals=2, args_size=10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc #3 // String 使用try、catch處理檢查異常5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: new #5 // class com/hammajang/springbootdemo/entity/MyException11: dup //s 復制異常對象引用12: invokespecial #6 // Method com/hammajang/springbootdemo/entity/MyException."<init>":()V15: athrow // 拋出MyException異常16: astore_1 // 將捕獲的異常對象存儲在局部變量表中17: new #7 // class java/lang/RuntimeException20: dup // 復制異常對象引用21: aload_1 // 將局部變量中的異常對象加載到操作數棧22: invokespecial #8 // Method java/lang/RuntimeException."<init>":(Ljava/lang/Throwable;)V25: athrow // 拋出RuntimeException異常Exception table:from to target type8 16 16 Class com/hammajang/springbootdemo/entity/MyException
? ? ? ? Exception table含義:如果執行指令8-指令16(try塊)的過程中拋出了MyException類型的異常,則跳轉到指令16(finally塊)繼續執行。
? ? ? ? (2)try-catch-finally字節碼解析
? ? ? ? JavaCodes:
public class TestMyException{public static void main(String[] args) {System.out.println("使用try、catch捕獲自定義檢查異常");try {throw new MyException();} catch (MyException e) {//將自定義檢查異常封裝成非檢查異常throw new RuntimeException(e);} finally {System.out.println("TestMyException finally code...");}}
}
? ? ? ? ByteCodes:
Code:stack=3, locals=3, args_size=10: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc #3 // String 使用try、catch捕獲自定義檢查異常5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: new #5 // class com/hammajang/springbootdemo/entity/MyException11: dup // 復制異常對象引用12: invokespecial #6 // Method com/hammajang/springbootdemo/entity/MyException."<init>":()V15: athrow // 拋出異常16: astore_1 // 將異常對象存儲在局部變量表中17: new #7 // class java/lang/RuntimeException20: dup // 復制異常對象引用21: aload_1 // 將局部變量表中的異常對象加載到操作數棧22: invokespecial #8 // Method java/lang/RuntimeException."<init>":(Ljava/lang/Throwable;)V25: athrow // 拋出異常26: astore_2 // 將異常對象存儲在局部變量表中27: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;30: ldc #9 // String TestMyException finally code...32: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V35: aload_2 36: athrowException table:from to target type8 16 16 Class com/hammajang/springbootdemo/entity/MyException8 27 26 any
????????Exception table:加了finally塊后,異常表中多了一條記錄,表示從指令8-指令27(try塊、catch塊)如果拋出了任意類型(any)的異常,都會跳轉到指令26(finally塊)繼續執行。
finally塊解析:
? ? ? ? (1)模擬空指針異常
? ? ? ? JavaCodes:
public class TestMyException{public static void main(String[] args) {MyException exception = null;try{exception.test();}catch (Exception e){System.out.println(e);}finally {exception = new MyException();}}
}
class MyException extends Exception{public void test(){System.out.println("test method");}
}
? ? ? ? ByteCodes:
Code:stack=2, locals=4, args_size=10: aconst_null1: astore_12: aload_13: invokevirtual #2 // Method com/hammajang/springbootdemo/entity/MyException.test:()V6: new #3 // class com/hammajang/springbootdemo/entity/MyException9: dup10: invokespecial #4 // Method com/hammajang/springbootdemo/entity/MyException."<init>":()V13: astore_114: goto 4717: astore_218: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;21: aload_222: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V25: new #3 // class com/hammajang/springbootdemo/entity/MyException28: dup29: invokespecial #4 // Method com/hammajang/springbootdemo/entity/MyException."<init>":()V32: astore_133: goto 4736: astore_337: new #3 // class com/hammajang/springbootdemo/entity/MyException40: dup41: invokespecial #4 // Method com/hammajang/springbootdemo/entity/MyException."<init>":()V44: astore_145: aload_346: athrow47: returnException table:from to target type2 6 17 Class java/lang/Exception2 6 36 any17 25 36 any
有三處地方的指令需要我們注意:
? ? ? ? 1、指令6、指令9、指令10(try)
? ? ? ? 2、指令25、指令28、指令29(catch)
? ? ? ? 3、指令37、指令40、指令41(finally)
? ? ? ? 這三處地方的指令都執行同一個操作:創建并初始化MyException對象。
????????從字節碼層面我們就可以得知為什么finally塊的代碼一定會執行了,因為在將.java文件編譯成.class字節碼文件時,編譯器會將finally塊的代碼放在try塊、catch塊的末尾。
? ? ? ? 在Exception table中我們也可以看到,在指令2-指令6(try塊)、指令17-25(catch塊)執行時,如果拋出了任意(any)類型的異常,就會跳轉到指令36(finally塊)繼續執行。
? ? ? ??