返回

字节码剖析 try-return-finally 执行顺序

前端

1. try-catch-finally语法的执行顺序

众所周知,Java中try-catch-finally代码块的执行顺序为:

  • 首先执行try块中的代码
  • 如果在try块执行期间发生了异常,那么立即终止try块的后续代码,执行匹配的catch块
  • 无论是否有异常发生,finally块总会被执行

2. try-return-finally执行顺序的争议

但是,如果在try块中使用return语句返回了,那么finally块是否还会执行?这个问题在Java社区引起了激烈的争论。

正方观点认为:

  • finally块总是会执行,即使在try块中使用了return语句
  • 这是因为finally块的执行与try块中的异常无关,而是与方法调用或构造器调用相关
  • 因此,无论是否发生异常,finally块总会被执行

反方观点认为:

  • 如果在try块中使用了return语句,那么finally块不会被执行
  • 这是因为return语句会立即终止当前方法或构造器的执行,而finally块是在方法或构造器执行结束后才执行的
  • 因此,如果在try块中使用了return语句,那么方法或构造器的执行就会立即终止,而finally块也就不会被执行

3. 字节码剖析

为了弄清这个问题的真相,我们不妨对一段简单的Java代码进行字节码剖析。

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

    private static int test() {
        try {
            return 1;
        } finally {
            System.out.println("finally");
        }
    }
}

使用javap命令对该代码进行字节码反编译,得到如下结果:

public class FinallyTest {
    public static void main(String[] args);
    Code:
       0: invokestatic  #2                  // Method test:()I
       3: invokestatic  #3                  // Method java/lang/System.out:println(I)V
       6: return

    private static int test();
    Code:
       0: iconst_1
       1: ireturn
       2: astore_0
       3: aload_0
       4: athrow
}

从反编译结果中,我们可以看到,test()方法的字节码指令序列如下:

  • iconst_1:将整数1压入操作数栈
  • ireturn:从方法中返回整数1,并将操作数栈清空
  • astore_0:将异常对象压入局部变量表中的索引0处
  • aload_0:从局部变量表中的索引0处加载异常对象
  • athrow:将异常对象抛出

其中,iconst_1和ireturn指令位于try块中,astore_0、aload_0和athrow指令位于finally块中。

从字节码指令序列中,我们可以清楚地看到,try块中的代码确实会在finally块之前执行。但是,如果在try块中使用了return语句,那么finally块中的代码并不会被执行。

这是因为ireturn指令会立即终止方法的执行,而finally块是在方法执行结束后才执行的。因此,如果在try块中使用了return语句,那么方法的执行就会立即终止,而finally块也就不会被执行。

4. 结论

综上所述,我们可以得出结论:如果在try块中使用了return语句,那么finally块不会被执行。这是因为return语句会立即终止当前方法或构造器的执行,而finally块是在方法或构造器执行结束后才执行的。

因此,在编写代码时,我们需要注意,如果需要在try块中返回一个值,那么就不要在finally块中执行任何操作。否则,finally块中的代码将不会被执行。