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 语句块。
原理
通过分析后面的反汇编代码,我们可以看出来:
- finally 基本都是要执行的,除非暴力的 System.exit 或者线程提前终结了。
- catch 的实现方法是异常表。多个 try...catch,嵌套的 try...catch 共用一个异常表,不同方法不共用异常表。这个你可以检验一下。
- try 即使 return,finally 都还执行的原因,是 java 在编译的时候,在 try 语句块的 return 之前插入代码。而且,return 的内容可以被 finally 的 return 覆盖。
- 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
}