关于 Java finally 执行顺序 -- 修改版

前言

之前写了一篇 关于 try-catch-finally 执行顺序 的文章,但是写的有些繁琐了,这里重新写一下。

题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Test {
public int aaa() {
int x = 1;

try {
return ++x;
} catch (Exception e) {

} finally {
++x;
}
return x;
}

public static void main(String[] args) {
Test t = new Test();
int y = t.aaa();
System.out.println(y);
}
}

我第一次看到这个题,得出了几个问题:

  • 如果在 try 语句块里有 return 语句,那么 finally 还会执行吗?
  • 如果执行那应该是怎样的执行过程呢?

首先这道题的运行结果是 2,可能跟你想的不一样吧,别急,下面我会慢慢解释的。

在学习 Java 基础的时候,老师就讲过,try-catch-finally 中的 finally 语句块一定会被执行,那么我们来 debug 一下:

初始状态:

此时运行到了 return ++x; 但还没执行该语句,目前 x 的值为 1.

这时发现跳到了 finally 语句块中,且 return ++x++ 操作已经执行,但没有进行 reutrn,目前 x 的值为 2:

执行完 finally 语句块中的 ++x 操作后,又回到了 return 中,此时 x 的值为 3:

但根据最后的运行结果,我们发现其实真正 reutrn 的是 2,那么如何解释这一点呢?

其实在 官方的 JVM 规范 中对这一部分有说明:

If the try clause executes a return, the compiled code does the following:

  1. Saves the return value (if any) in a local variable.
  2. Executes a jsr to the code for the finally clause.
  3. Upon return from the finally clause, returns the value saved in the local variable.

简单翻译如下:

如果 try 语句里有 return,那么代码的行为如下:

  1. 如果有返回值,就把返回值保存到局部变量中
  2. 执行 jsr 指令跳到 finally 语句里执行
  3. 执行完 finally 语句后,返回之前保存在局部变量表里的值

看完这个应该就能理解为什么返回的是 2 了,

但要注意的是:

  • 如果在 finally 语句块中也使用了 return 语句,那么会忽略 try 中的 return 语句,而执行 finally 中的 return。
  • 如果 try 中 return 的是引用数据类型,那么 finally 中的操作可能会影响最终的 return 值,因为对于引用数据类型,暂存到局部变量里的是它的地址值。