finally关键字一般和try…catch…连用,一般不作妖的话(比如中途退出虚拟机等,这个不在讨论范围内,哈哈!),finally代码块是会执行的。
这篇博客主要讲一下有关finally的一些知识点。
话不多说,上例子↓
public class demo1 {
public static void main(String[] args) {
int i=0;
try {
i=10;
}catch (Exception e){
i=20;
}finally {
i=30;
}
System.out.println(i);
}
}
显而易见,结果为30,反编译获取部分字节码
0: iconst_0
1: istore_1
2: bipush 10
4: istore_1
5: bipush 30
7: istore_1
8: goto 27
11: astore_2
12: bipush 20
14: istore_1
15: bipush 30
17: istore_1
18: goto 27
21: astore_3 //会利用一个没有名字的局部变量表中的槽位来存储异常对象
22: bipush 30
24: istore_1
25: aload_3
26: athrow
27: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintSt
ream;
30: iload_1
31: invokevirtual #4 // Method java/io/PrintStream.println:(I)V
34: return
Exception table:
from to target type
2 5 11 Class java/lang/Exception
2 5 21 any #剩余的异常类型,比如error
11 15 21 any
其中Exception table的内容相当于监视器,举个例子
from to target type
2 5 11 Class java/lang/Exception
它监视[2,5)的字节码,一旦发生错误,则根据对应的错误类型跳转到不同的地方,假如[2,5)的地方发生了Exception类的错误,则跳转到11: astore_2
,如果发生了其他的比如error错误,则跳转到21: astore_3
,然后往下执行,
最后26: athrow
抛出异常!!(第三个例子不抛出异常)
总结:
- 从字节码可以看到finally中的代码被复制了3份,分别放入try流程,catch流程,以及catch剩余的异常类型流程
第二个例子:
public static void main(String[] args) {
int result=test();
System.out.println(result);
}
public static int test(){
try {
return 10;
}finally {
return 20;
}
}
test部分的字节码
public static int test();
descriptor: ()I
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=2, args_size=0
0: bipush 10 //10放入栈顶
2: istore_0 //10-> slot 0 (从栈顶移除了)
3: bipush 20 //20放入栈顶
5: ireturn //返回栈顶 int(20)
6: astore_1 //catch any -> slot 1
7: bipush 20//20 放入栈顶
9: ireturn //返回栈顶 int(20)
Exception table:
from to target type
0 3 6 any
所以结果为20.
总结:
- 由于finally中的ireturn被插入了所有可能的流程,因此返回结果肯定以finally的为准
第三个例子
public static void main(String[] args) {
int result=test();
System.out.println(result);
}
public static int test(){
try {
int i=1/0;
return 10;
}finally {
return 20;
}
}
它的输出结果为20,不会抛出异常。字节码如下:
public static int test();
descriptor: ()I
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=0
0: iconst_1
1: iconst_0
2: idiv
3: istore_0
4: bipush 10
6: istore_1
7: bipush 20
9: ireturn
10: astore_2
11: bipush 20
13: ireturn
Exception table:
from to target type
0 7 10 any
总结:
- 跟第一个例子相比,没有athrow,这告诉我们:如果在finally中出现了return,会吞掉异常。
第四个例子
public static void main(String[] args) {
int result=test();
System.out.println(result);
}
public static int test(){
int i=10;
try {
return i;
}finally {
i=20;
}
}
结果为10,理由看字节码:
public static int test();
descriptor: ()I
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=3, args_size=0
0: bipush 10 #10 放入栈顶
2: istore_0 #10->i
3: iload_0
4: istore_1 #暂存到slot 1,目的是为了固定返回值
5: bipush 20
7: istore_0
8: iload_1
9: ireturn
10: astore_2
11: bipush 20
13: istore_0
14: aload_2
15: athrow
Exception table:
from to target type
3 5 10 any
有错误的话望指正
本文章参考:黑马程序员JVM.