【异常】浅析异常体系及为什么一定会执行finally块

异常体系:

        (1)所有异常(Exception)、错误(Error)都继承自异常中的基类:Throwable。而异常又可以分为检查异常(Checked Exception)、非检查异常(Unchecked Exception)两大类。

【异常】浅析异常体系及为什么一定会执行finally块_第1张图片

        (2)检查异常在编译期间由编译器检查的异常,编译器确保这些异常在编译期被处理,意味着不能直接使用关键字throw抛出异常,要么使用try、catch处理异常,要么在方法声明上使用throws关键字提醒方法调用者该方法可能会抛出的异常,让方法调用者处理异常。Exception从属子类中,除了RuntimeException类及其从属子类,其它子类都属于这一类型的异常。

        直接抛出检查异常:

【异常】浅析异常体系及为什么一定会执行finally块_第2张图片

        编译器直接报错,提示该异常需要处理。

        使用try、catch捕获异常:

【异常】浅析异常体系及为什么一定会执行finally块_第3张图片

        编译器不会报错,因为在catch块捕获了异常,并进行了处理(e.printStackTrace())。

        在方法签名上抛出异常:

【异常】浅析异常体系及为什么一定会执行finally块_第4张图片

        编译器不会报错,因为在调用该方法时,编译器会强制要求方法调用者使用try、catch捕获异常进行处理,或者继续通过throws关键字往上抛,让更上一层的方法调用者进行处理。

        (3)非检查异常编译器不会在编译期间就检查这类异常,直接抛出这类异常编译器不会报错,只有在程序运行时才可能抛出的异常。RuntimeException及其从属子类、Error及其从属子类都属于这类异常。

        直接抛出非检查异常:

【异常】浅析异常体系及为什么一定会执行finally块_第5张图片

        编译器不会报错,因为非检查异常只会在程序运行时才会进行相应处理。

        (4)如果想自定义检查异常,那么让类直接继承Exception类即可;如果想自定义非检查异常,那么让类直接继承自RuntimeException即可。

        自定义检查异常

【异常】浅析异常体系及为什么一定会执行finally块_第6张图片

        自定义非检查异常

【异常】浅析异常体系及为什么一定会执行finally块_第7张图片

异常处理机制:

        (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=1
         0: 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;)V
         8: new           #5                  // class com/hammajang/springbootdemo/entity/MyException
        11: dup                               //s 复制异常对象引用
        12: invokespecial #6                  // Method com/hammajang/springbootdemo/entity/MyException."":()V
        15: athrow                            // 抛出MyException异常
        16: astore_1                          // 将捕获的异常对象存储在局部变量表中
        17: new           #7                  // class java/lang/RuntimeException
        20: dup                               // 复制异常对象引用
        21: aload_1                           // 将局部变量中的异常对象加载到操作数栈
        22: invokespecial #8                  // Method java/lang/RuntimeException."":(Ljava/lang/Throwable;)V
        25: athrow                            // 抛出RuntimeException异常
      Exception table:
         from    to  target type
             8    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=1
         0: 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;)V
         8: new           #5                  // class com/hammajang/springbootdemo/entity/MyException
        11: dup                               // 复制异常对象引用
        12: invokespecial #6                  // Method com/hammajang/springbootdemo/entity/MyException."":()V
        15: athrow                            // 抛出异常
        16: astore_1                          // 将异常对象存储在局部变量表中
        17: new           #7                  // class java/lang/RuntimeException
        20: dup                               // 复制异常对象引用
        21: aload_1                           // 将局部变量表中的异常对象加载到操作数栈
        22: invokespecial #8                  // Method java/lang/RuntimeException."":(Ljava/lang/Throwable;)V
        25: 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;)V
        35: aload_2                            
        36: athrow
      Exception table:
         from    to  target type
             8    16    16   Class com/hammajang/springbootdemo/entity/MyException
             8    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=1
         0: aconst_null
         1: astore_1
         2: aload_1
         3: invokevirtual #2                  // Method com/hammajang/springbootdemo/entity/MyException.test:()V
         6: new           #3                  // class com/hammajang/springbootdemo/entity/MyException
         9: dup
        10: invokespecial #4                  // Method com/hammajang/springbootdemo/entity/MyException."":()V
        13: astore_1
        14: goto          47
        17: astore_2
        18: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
        21: aload_2
        22: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
        25: new           #3                  // class com/hammajang/springbootdemo/entity/MyException
        28: dup
        29: invokespecial #4                  // Method com/hammajang/springbootdemo/entity/MyException."":()V
        32: astore_1
        33: goto          47
        36: astore_3
        37: new           #3                  // class com/hammajang/springbootdemo/entity/MyException
        40: dup
        41: invokespecial #4                  // Method com/hammajang/springbootdemo/entity/MyException."":()V
        44: astore_1
        45: aload_3
        46: athrow
        47: return
      Exception table:
         from    to  target type
             2     6    17   Class java/lang/Exception
             2     6    36   any
            17    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块)继续执行。

        

你可能感兴趣的:(java,开发语言)