原题
首先来看看这道题:
|
|
执行结果是
,刚看到这个结果,我是比较诧异的,利用 IDEA 的单步调试也没弄明白,来看看我当时的思路:
- ABAB
- 首先输出
中的输出语句,输出- try
- "A"
- 由于
下还有的- try
语句,所以执行- finally
中的输出语句,输出- finally
- "B"
- 将
赋值给变量- "B"
- s
- 回到
中的- try
语句,再次将- return
赋值给- "A"
,将- s
最为返回值,返回- "A"
方法中。(此时- main
)- s = "A"
- 回到
方法中的第一行输出语句,输出- main
,然后执行- "A"
方法中的第二条输出语句,输出- main
- "A"
所以,结果不应该是 “ABAA” 么,因为 return 中已经将 “A” 赋值给 s 了,那么 s 的值不应该是 “A” 么,这时返回值就是 “A”,再怎么第二条输出语句也不能是 “B” 呀,可看着开发工具输出的结果,我也很无奈,我当时是这个表情。。。
但我相信开发工具是不会骗我的,一定是我自己的理解有问题,然后自己又写了几个类似的例子来测试,最后得出的结论是:finally 语句块在 return 语句之后,return 返回之前执行,可能这句话不是那么容易理解,来看下面的例子吧,看完我相信你应该能明白我这句话是什么意思了。
例一
证明:
- finally 语句块在 return 语句之后,return 返回之前执行。
|
|
运行结果:
- try block
- finally block
- b>25, b = 100
- 100
可以根据运行结果得知,在
语句块执行之前,
- finally
中的
- return b += 80;
这部分已经运行,但是没有返回,然后等
- b + =80;
执行完以后才返回结果的。
- finally
例二
如果觉得这个
还不足以说明这个情况的话,下面再加个例子加强证明结论:
- 例 1
|
|
运行结果:
- try block
- return statement
- finally block after
- return
这下总明白了吧,执行
之前将
- finally
后的
- return
方法先执行了,然后再执行的
- test12()
语句。
- finally
拓展问题一
前面两个例子证明了原题得出的结论,但是这个结论好像还不能足矣证明原题中的结果为什么是
,还有一个新问题:
- "ABAB"
如果,finally 中对 return 返回的变量进行了修改,那么 return 返回的是修改前的值还是修改后的值?
我们在
的
- 例 1
语句块中加上一句
- finally
,那么结果又会是什么呢?
- b = 10;
|
|
运行结果:
- try block
- finally block
- b>25, b = 100
- 100
可以看到
的值并没有被
- return
中的语句改变,其实如果这样得出结论:finally 中对 return 返回的变量进行了修改,并不会影响 try 中 return 的值,是不负责任的,因为我们只考虑了基本数据类型,如果是引用数据类型,还会是这种结果么?
- finally
扩展问题二
接
的又一个问题,对于引用数据类型的情况:
- 拓展问题 1
|
|
运行结果:
- Finally
可以看到,对于基本数据类型和引用数据类型,结论是不同的,再次总结下结论吧:
- 对于基本数据类型,
语句块中对- finally
语句块的- try
的变量进行了修改,- return
的则是修改前的值。- return
- 对于引用数据类型,
语句块中对- finally
语句块的- try
的变量进行了修改,- return
的则是修改后的值。- return
看到这里,不知道你有没有想到这个结论与 Java 的方法调用时的所谓的
和
- 值传递
的结论有些类似。
- 引用传递
既然得出了这个结论,那么这个结论是必然的情况么?一定正确么?别急,来看下一个例子。
拓展问题三
对于
我再做一个增强版的,其实就是在第 17 行之下添加一条语句
- 拓展问题 2
:
- stu = null
|
|
运行结果:
- Finally
这时,你可能都有骂人的冲动了,What ?咋回事,咋什么结论都不对呢,不是说好的引用数据类型会改变
的值么,这里都将
- return
修改为
- stu
了,怎么还能
- null
了
- return
呢?
- "Finally"
别激动,别激动,不卖关子了,我直接说了:
先说基本数据类型,由于基本数据类型是 值传递 ,所以在
里的
- try
执行的时候,就先将变量的值隐性的作为最终返回的值。
- return
同样的,对于引用数据类型,只是将该变量所指向的内存地址隐性的作为最终返回的值,所以即使将
,也无所谓了,因为早在执行
- stu = null
之前,
- finally
里的
- try
就已经拿到了
- return
所指向的地址。
- stu
这里多说一句,其实 Java 里都是值传递,只不过基本数据类型的值是真正的值,而引用数据类型是地址值而已。(如果你看这句话更晕的话,就先跳过去这句话,反正本篇文章也不是为了解释这个的。)
更深的思考
问:如果
与
- try
中都有
- finally
语句,那么到底会返回哪一个呢?
- return
会返回
中的
- finally
。
- return
问:如果
里有
- try
语句,整个
- return
块之外也有一个
- try-catch-finally
语句,那么哪个会执行,一定是这样么?
- return
在
里没有发生异常的情况下,是
- try
里的
- try
会执行,但发生了异常,则反之。
- return
问:如果
中有
- catch
语句呢?当然只有在异常的情况下才有可能会执行,那么是在
- return
之前就返回吗?
- finally
当发生异常后,
中的
- catch
执行情况与未发生异常时
- return
中
- try
的执行情况完全一样。
- return
最终总结
finally 块的语句在 try 或 catch 中的 return 语句执行之后返回之前执行,且 finally 里的修改语句可能影响也可能不影响 try 或 catch 中 return 已经确定的返回值,若 finally 里也有 return 语句则覆盖 try 或 catch 中的 return 语句直接返回。
本文参考
- Java finally语句到底是在return之前还是之后执行?
- finally执行顺序面试题