finally与return之间的关系

定论

问:finally语句一定会执行吗?
答:

  1. 如果没有执行相应的try语句则不会执行。

  2. 在try语句中如果调用System.exit(0)方法则不会执行。

问:finally会在什么时候执行?
答:如果在try/catch语句中调用转移指令例如:return,break,continue,throw等。则会在转移指令前执行。

总结

finally与return之间的关系

如果在finally中含有return语句,那么try/catch语句的return还有作用吗?

先看一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* Created by gavin on 15-9-2.
*/
public class FinallyTest {
public static void main(String[] args){
System.out.println(test1()); //3
System.out.println(test2()); //3
System.out.println(test3()); //2
System.out.println(test4()); //2
}
public static int test1()
{
int i = 1;
try {
i = 2;
return i;
}finally {
i++;
return i;
}
}
public static int test2()
{
int i = 1;
try {
i = 2;
return i;
}finally {
i = 3;
return i;
}
}
public static int test3()
{
int i = 1;
try {
i = 2;
return i;
}finally {
i++;
}
}
public static int test4()
{
int i = 1;
try {
i = 2;
return i;
}finally {
i = 3;
}
}
}

如果你对java内存布局不是很清楚,请看这篇文章:java虚拟机类加载机制和字节码执行引擎

重点关注运行时栈帧结构(局部变量表槽操作数栈)。

上边的代码非常简单,来看一下字节码指令吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
public static int test1();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=3, args_size=0
0: iconst_1 //定义一个常量1入栈到操作数栈
//栈1 0: 1:
1: istore_0 //出栈,存储到局部便量表槽0
//栈 0:1 1:
2: iconst_2 //定义一个常量2入栈到操作数栈
//栈2 0:1 1:
3: istore_0 //出栈,存储到局部变量表槽0
//栈 0:2 1:
4: iload_0 //从局部便量表槽0入栈到操作数栈
//栈2 0:2 1:
5: istore_1 //出栈,存储到局部变量表槽1
//栈 0:2 1:2
6: iinc 0, 1 //局部变量表槽0变量加1
//栈 0:3 1:2
9: iload_0 //从局部变量表槽0入栈到操作数栈
//栈3 0:3 1:2
10: ireturn //结束,返回
//栈3 0:3 1:2
11: astore_2
12: iinc 0, 1
15: iload_0
16: ireturn
public static int test2();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=3, args_size=0
0: iconst_1 //定义一个常量1入栈到操作数栈
//栈1 0: 1:
1: istore_0 //出栈,存储到局部便量表槽0
//栈 0:1 1:
2: iconst_2 //定义一个常量2入栈到操作数栈
//栈2 0:1 1:
3: istore_0 //出栈,存储到局部变量表槽0
//栈 0:2 1:
4: iload_0 //从局部变量表槽0入栈
//栈2 0:2 1:
5: istore_1 //出栈,存储到局部变量表槽1
//栈 0:2 1:2
6: iconst_3 //定义一个常量3入栈
//栈3 0:2 1:2
7: istore_0 //出栈,存储到局部便量表槽0
//栈 0:3 1:2
8: iload_0 //从局部变量表槽0入栈
//栈3 0:3 1:2
9: ireturn //结束,返回
//栈3 0:3 1:2
10: astore_2
11: iconst_3
12: istore_0
13: iload_0
14: ireturn
public static int test3();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=3, args_size=0
0: iconst_1 //定义一个常量1入栈到操作数栈
//栈1 0: 1:
1: istore_0 //出栈,存储到局部便量表槽0
//栈 0:1 1:
2: iconst_2 //定义一个常量2入栈到操作数栈
//栈2 0:1 1:
3: istore_0 //出栈,存储到局部变量表槽0
//栈 0:2 1:
4: iload_0 //从局部变量表槽0入栈
//栈2 0:2 1:
5: istore_1 //出栈,存储到局部变量表槽1
//栈 0:2 1:2
6: iinc 0, 1 //局部变量表槽0变量加一
//栈 0:3 1:2
9: iload_1 //从局部变量表槽1入栈
//栈2 0:3 1:2
10: ireturn //结束,返回
//栈2 0:3 1:2
11: astore_2
12: iinc 0, 1
15: aload_2
16: athrow
public static int test4();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=3, args_size=0
0: iconst_1 //定义一个常量1入栈到操作数栈
//栈1 0: 1:
1: istore_0 //出栈,存储到局部便量表槽0
//栈 0:1 1:
2: iconst_2 //定义一个常量2入栈到操作数栈
//栈2 0:1 1:
3: istore_0 //出栈,存储到局部变量表槽0
//栈 0:2 1:
4: iload_0 //从局部变量表槽0入栈
//栈2 0:2 1:
5: istore_1 //出栈,存储到局部变量表槽1
//栈 0:2 1:2
6: iconst_3 //定义一个常量3入栈到操作数栈
//栈3 0:2 1:2
7: istore_0 //出栈,存储到局部变量表槽0
//栈 0:3 1:2
8: iload_1 //从局部变量表槽1入栈
//栈2 0:3 1:2
9: ireturn //结束,返回
//栈2 0:3 1:2
10: astore_2
11: iconst_3
12: istore_0
13: aload_2
14: athrow

我们看到:

在finally中没有return时,栈中最后存储的数据是try/catch中操作后数据。即finally操作后的数据存储到其他槽中,而后再加载try/catch操作后的数据。

而在finally中含有return时,栈中最后存储的数据是finally中操作后的数据。即finally操作后的数据存储到其他槽中,而后加载的是其他槽(finally)中的数据。

也就是说:如果finally中不含有return语句,finally对try/catch操作的八大基础类型不会再加载到操作数栈中。

如果返回值是对象引用,finally中的return还有待考据。

参考:关于 Java 中 finally 语句块的深度辨析