深入分析Java的try-catch-finally(字节码)

438 阅读2分钟

演示案例

public int zou() {
    int a = 0;

    try {
        a = 1;
        return a;
    } catch (Exception c) {
        a = 2;
        return a;
    } finally {
        a = 3;
        return a;
    }

}

想必这段代码大家都能够非常轻松的理解

大家思考一下我们在工作中是不是有遇到过这种疑惑 :finally一定会执行吗?答案肯定是不会的。

先贴个答案

  • 在开始执行fainlly代码前调用了System.exit()
  • JVM崩溃了
  • 当前执行的线程被杀死
  • 操作系统关机
  • 无限循环或阻塞
  • 除去这些极端情况finally一般都会执行

为什么finally在正常情况下一定会执行呢?

问题一:finally是如何工作的

通过查看字节码就可以知道: image.png

这个问题非常简单,JVM就是在程序编译的时候会将finally的代码复制到try以及catch代码块的最后。复制的时候会把return给提取出来,并作为逻辑分支的最后一条语句(finally的return要优先于try、catch)

还有一种情况:

image.png 如果finally中去改变try的return会怎么样?3?1?答案是1,finally中的赋值不再会影响reutrn的值

image.png

这个时候返回的就不是a了,而是a的复制对象

那么在这个时候如果在catch代码中出现异常该怎么办呢? 在了解这个问题的时候需要需要先了解catch代码是如何工作的。

问题二:catch是如何工作的

catch的执行需要依靠一个东西,“异常表”

image.png

image.png

根据异常表的说法就是在try执行逻辑的范围中如果出现了指定的异常那么就跳转到指定的PC行执行, catch的逻辑大致就是靠这个实现的,那么具体是如何是实现的就靠读者扩展了

总结

现在finally的执行逻辑就很清晰了:

finally:

如果是finally的话,执行的逻辑就是靠在代码编译的时候将finally代码块中的内容拼接到try和catch中。

catch:

如果是catch的话,在程序出现错误的情况就依靠异常表判断出错地点是否在异常变量表中记载,如果有记载就通过异常变量表记录跳转到指定的PC行去执行。