Java 中 return, try catch finally 的执行顺序

return 语句对应两条汇编指令:

  1. 将返回值写入到某个寄存器中
  2. 执行 ret 指令返回到调用函数的位置。

在 Java 中,在这两个指令之间可以插入很多其他的指令,比如 finally 代码块中的指令。对于有 finally 语句的代码,执行到 return 语句时,先将返回值放入到返回值寄存器中,然后继续执行 finally 代码块中的指令。理解了这一点就非常容易理解下面这些代码的执行顺序:

public static int getInt() {
    int a = 10;
    try {
        return a;
    } finally {
        a = 40;
    }
}

输出: 10

执行到 try 代码块中的 return a 语句时,先将 a 的值,也就是 10 放入到返回值寄存器中,然后执行 finally 代码块中内容。由于 finally 代码块中的指令并没有修改返回值寄存器里面的内容,所以寄存器里面的值还是 10。所以 getInt() 的返回值是 10

再看一段在 finally 中修改返回值代码:

public static int getInt() {
    int a = 10;
    try {
        return a;
    } finally {
        a = 40;
        return a;
    }
}

输出: 40

根据我们上面的分析: try 代码块中的 return a 先将 a 的值,也就是 10 放入到返回值寄存器中,然后执行 finally 代码块中的内容,由于 finally 代码块中也有 return 语句,所以将 a 的值,也就是 40 放入到寄存器中,因此 getInt() 的返回值是 40。

再看下面的这段代码,返回值是多少呢?

public static int getInt() {
    int a = 10;
    try {
        System.out.println(a / 0);
        return a;
    } catch (ArithmeticException e) {
        a = 30;
        return a;
    } finally {
        a = 40;
    }
}

输出: 30

要理解为什么输出是 30, 就得知道 try, catch 和 fianlly 代码块的执行顺序了。只需要记住一点即可: fianlly 里面的语句永远是最后执行的。

由于 try 中发生异常,所以执行 catch 代码块中代码,执行到 return a; 语句时,将 a 的值也就是 30 放入到返回值寄存器中,因为有 finally 代码块,所以执行 finally 代码块中的语句,由于 finally 代码块中并没有 return 语句,所以返回值寄存器里面的内容没有发生变化,还是 30, 所以 getInt() 的返回值是 30。

再看一段在 finally 中修改返回值代码:

public static int getInt() {
    int a = 10;
    try {
        System.out.println(a / 0);
        return a;
    } catch (ArithmeticException e) {
        a = 30;
        return a;
    } finally {
        a = 40;
        return a;
    }
}

输出: 40

这段代码就不解释了。

总结

  1. return 语句对应两条汇编指令: 将返回值放入到返回值寄存器中 + 返回到调用函数的地方
  2. return 两条汇编指令之间可以插入 fianlly 代码块中的指令。
  3. 在执行 finally 代码块中的指令之前,会先执行 return 的第一条汇编指令: 将返回值写入到返回值寄存器中
  4. finally 其实是语法糖,让代码更为的简洁,避免代码冗余。如果没有 fianlly 语法糖,就需要将 finally 代码块中的指令在 trycatch 中都放一份。
使用 Hugo 构建
主题 StackJimmy 设计