finally 语句块是否一定会被执行?

结论

结论:不一定,存在两种可能的情况。第一种,调用了 System.exit,第二种,finally 语句位于一个线程中,但是这个线程随着主线程的终结而终结了。代码请看 example1.另外值得注意的是,即使在 try 中 return 了,还是会执行后面的 finally。

代码:

输出:

原理

那 try, catch, finally 是如何实现的呢?下面我们用反汇编看看。

反汇编命令 javap -c -p Example1.class,完整的反汇编代码放到了最后。

异常表

我们可以看到在 test 函数的最后,有一个异常表。表头有四列,from to target type。一共有三行。第一行是 try 语句块中,如果发生 Exception 这个异常,那么将跳转到第 48 行执行。第二,第三行,跳转到 finally 中去执行。

那这个 any 是什么呢?如果还没有看反汇编的代码,你可能会觉得这就是 finally 的实现原理了,执行完 try/catch 块之后就跳转到 finally 语句块去执行。可是,看看反汇编的代码,就会发现这个函数中,有三个 finally 语句块!在 try 后面有一个,在 catch 后面还有两个。所以,这里我猜是除了 catch 之外的其他异常,如果在 try 和 catch 中发生了没有捕获的异常,那么就去执行 finally 语句块。

原理

通过分析后面的反汇编代码,我们可以看出来:

  1. finally 基本都是要执行的,除非暴力的 System.exit 或者线程提前终结了。
  2. catch 的实现方法是异常表。多个 try...catch,嵌套的 try...catch 共用一个异常表,不同方法不共用异常表。这个你可以检验一下。
  3. try 即使 return,finally 都还执行的原因,是 java 在编译的时候,在 try 语句块的 return 之前插入代码。而且,return 的内容可以被 finally 的 return 覆盖。
  4. try, catch 没有捕获的异常,会在 finally 中抛出,athrow 指令。所以不要在 finally 中 return,否则这些没有捕获的异常将不会被抛出。

完整的反汇编代码。

Compiled from "Example1.java"

public class example.Example1 {
  public example.Example1();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #2                  // Method test:()Lexample/Example1$Apple;
       3: astore_1
       4: aload_1
       5: invokevirtual #3                  // Method example/Example1$Apple.show:()V
       8: return

  private static example.Example1$Apple test();
    Code:
       0: iconst_0
       1: istore_0
       2: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       5: ldc           #5                  // String ִ���� try ����
       7: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      10: iconst_1
      11: istore_0
      12: iconst_2
      13: newarray       int
      15: astore_1
      16: aload_1
      17: iconst_5
      18: iconst_0
      19: iastore
      20: new           #7                  // class example/Example1$Apple
      23: dup
      24: iload_0
      25: invokespecial #8                  // Method example/Example1$Apple."<init>":(I)V
      28: astore_2
      29: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      32: ldc           #9                  // String ִ���� finally ����
      34: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      37: iconst_3
      38: istore_0
      39: new           #7                  // class example/Example1$Apple
      42: dup
      43: iload_0
      44: invokespecial #8                  // Method example/Example1$Apple."<init>":(I)V
      47: areturn
      48: astore_1
      49: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      52: ldc           #11                 // String ִ���� catch ����
      54: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      57: iconst_2
      58: istore_0
      59: new           #7                  // class example/Example1$Apple
      62: dup
      63: iload_0
      64: invokespecial #8                  // Method example/Example1$Apple."<init>":(I)V
      67: astore_2
      68: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      71: ldc           #9                  // String ִ���� finally ����
      73: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      76: iconst_3
      77: istore_0
      78: new           #7                  // class example/Example1$Apple
      81: dup
      82: iload_0
      83: invokespecial #8                  // Method example/Example1$Apple."<init>":(I)V
      86: areturn
      87: astore_3
      88: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      91: ldc           #9                  // String ִ���� finally ����
      93: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      96: iconst_3
      97: istore_0
      98: new           #7                  // class example/Example1$Apple
     101: dup
     102: iload_0
     103: invokespecial #8                  // Method example/Example1$Apple."<init>":(I)V
     106: areturn
    Exception table:
       from    to  target type
           2    29    48   Class java/lang/Exception
           2    29    87   any
          48    68    87   any
}
posted @ 2021-02-07 17:30  楷哥  阅读(1291)  评论(0编辑  收藏  举报