3

关于 try 和 finally 中的 return

首先我们来看一段代码:

public class Test {

    public static int inc() {
        int x = 1;

        try {
            return ++x; // 1*
        } catch (Exception e) {
            
        } finally {
            x++;
        }

        return x;
    }

    public static void main(String[] args) {
        System.out.println(inc());
    }
}

它的输出结果是多少呢?

2

我们走一下这个过程,x 的初始值是 1,然后进入到了 try 语句块中,在 1* 处,++x,x 会先自增,现在 x = 2,之后 return,return 是用来跳出当前方法,而 finally 是无论 try 语句发生了什么,都会执行的一个语句块,那么 try 中 return 和 finally 执行的顺序到底是谁先谁后呢?

我们来看 Oracle 文档中对 finally Block 的描述 The finally Block (The Java™ Tutorials > Essential Classes > Exceptions)

The finally Block

The finally block always executes when the try block exits. This ensures that the finally block is executed even if an unexpected exception occurs. But finally is useful for more than just exception handling — it allows the programmer to avoid having cleanup code accidentally bypassed by a return, continue, or break. Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated.

Note: If the JVM exits while the try or catch code is being executed, then the finally block may not execute. Likewise, if the thread executing the try or catch code is interrupted or killed, the finally block may not execute even though the application as a whole continues.

从这段解释中我们可以知道,当 try 语句块退出时,finally 语句块总是会执行,这保证了当有异常发生时,finally 语句块会被执行,不过 finally 语句块的作用不仅于此,它帮助程序员避免在执行 return or continue or break 时绕过清理代码,所以即使没有异常需要捕获,将清理代码放到 finally 语句块中也是一个好的选择。

需要注意的是,只有一种情况:如果在执行 try or catch 语句时 JVM 退出了,比如我们调用 System.exit,那么 finally 才不会被执行。

在 Java 语言规范 Chapter 14. Blocks and Statements 中也提到:

The preceding descriptions say "attempts to transfer control" rather than just "transfers control" because if there are any try statements (§14.20) within the method or constructor whose try blocks or catch clauses contain the return statement, then any finally clauses of those try statements will be executed, in order, innermost to outermost, before control is transferred to the invoker of the method or constructor. Abrupt completion of a finally clause can disrupt the transfer of control initiated by a return statement.

如果在 try or catch 语句块中包含 return 语句,那么 finally 语句会在其 return 之前执行。

根据以上描述,我们知道了 finally 语句块会在 try or catch 语句块执行前执行,那么当 x 自增后,会继续执行 finally 语句块中的内容,即 x++,那么此时 x = 3,可是这段程序的返回结果是 2 ,这又是为什么呢?

我们来看 JVM 规范中的描述 Chapter 4. The class File Format

Control can be transferred to the finally clause (the finally subroutine can be invoked) in several different ways. If the try clause completes normally, the finally subroutine is invoked via a jsr instruction before evaluating the next expression. A break or continue inside the try clause that transfers control outside the try clause executes a jsr to the code for the finally clause first. 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. 将 return 的值存到一个局部变量中
  2. 执行 jsr 指令到 finally 语句块中的代码
  3. 从 finally 语句返回时,返回在局部变量中保存的值

终于,谜团揭开了!原来在 finally 语句中执行完毕后,它会返回存在局部变量中的在 try 语句块中 return 的值,因此它返回的是 1* 处的 x,也就是返回的 2。

并且还需要注意的是,finally 中的 return 会覆盖 try 中的 return。

参考资料:

http://www.cnblogs.com/averey...
https://docs.oracle.com/javase


shelbylee
31 声望1 粉丝

Enjoy coding~