是否一定执行、执行时机
一切要从这道题目说起:
Java语言中的finally语句(块)一定会被执行吗?是在try语句返回之前还是之后执行?
由于对这个问题的一知半解,当时只回答了前半个问题是的,一定会执行,没有回答后半个问题;笔试结束后查资料…才发现事情没那么简单🤯
诚然,我的回答是错误的,以下两种情况的finally语句不会被执行:
try语句没有被执行;也说明了finally语句被执行的必要非充分条件是对应try语句被执行try语句中有停止JVM的语句;
后半个问题的答案是之前,即在try语句返回之前执行,可通过一个简单的例子来说明:
jshell> String test1() {
...> System.out.println("return statement");
...>
...> return "after return";
...> }
| created method test1()
jshell> String test2() {
...> try {
...> System.out.println("try block");
...>
...> return test1();
...> } finally {
...> System.out.println("finally block");
...> }
...> }
| created method test2()
jshell> test2()
try block
return statement
finally block
$1 ==> "after return"
更详细地说明:执行return语句之后,再执行finally语句,再返回调用者;
执行的影响
finally语句中的return语句
jshell> int test() {
...> int b = 20;
...> try {
...> System.out.println("try block");
...>
...> return b += 80;
...> } catch (Exception e) {
...>
...> System.out.println("catch block");
...> } finally {
...>
...> System.out.println("finally block");
...>
...> if (b > 25) {
...> System.out.println("b>25, b = " + b);
...> }
...>
...> return 200;
...> }
...> }
| modified method test()
jshell> test()
try block
finally block
b>25, b = 100
$2 ==> 200
说明finally的return语句的值被返回给调用者,即finally块中的return语句会覆盖try块中的return返回;
finally语句中对try语句返回值的修改
这里要分为两种情况,返回值是基本类型还是引用类型:
- 若是基本类型,修改无效;
- 若是引用类型,也要分为两种情况:
- 若是修改引用,则无效;
- 若是修改被引用的对象的内容,则有效;
通过两个例子来演示:
基本类型
jshell> int test() {
...> int b = 20;
...>
...> try {
...> System.out.println("try block");
...>
...> return b += 80;
...> } catch (Exception e) {
...> System.out.println("catch block");
...> } finally {
...> System.out.println("finally block");
...>
...> if (b > 25) {
...> System.out.println("b>25, b = " + b);
...> }
...>
...> b = 150;
...> }
...>
...> return 2000;
...> }
| modified method test()
jshell> test()
try block
finally block
b>25, b = 100
$3 ==> 100
让我们来探究一下这个例子的字节码:
截图自jclasslib工具

6-8行,将本地变量数组(local variable array)的第0个值(即20)加80,并被压入操作数栈,保存在第1个int值中19-22行,将150压入操作数栈,保存在本地变量数组的第0个值中,将本地变量数组的第1个值即(100)压入操作数栈,返回
故而语句b = 150;并没有影响到返回值;
引用类型
jshell> Map<String, String> test() {
...> Map<String, String> map = new HashMap<String, String>();
...> map.put("KEY", "INIT");
...> try {
...> map.put("KEY", "TRY");
...> return map;
...> } catch (Exception e) {
...> map.put("KEY", "CATCH");
...> } finally {
...> map.put("KEY", "FINALLY");
...> map = null;
...> }
...> return map;
...> }
| replaced method test()
jshell> test()
$4 ==> {KEY=FINALLY}
这个例子的字节码如图:

与上同理。