首先,我们要了解finally的实际中使用的场景——finally出自异常处理中的try{}catch{}finally{}语句块中,其基本的目的是确保无论异常是否发生,都必然会执行finally语句块中的内容。
下面,我来讲讲fianlly语句块的切实应用。
一、程序因代码或非个人因素终止时finally代码不执行。
这里的非个人因素是指遇到设备关机或硬件设备损坏等原因导致程序无法再正常执行,这是不可控的,不多做关注。
因代码原因终止的情况有很多,有:调用System.exit()函数,调用halt()函数,try代码中因循环溢出导致系统崩溃和守护线程的原因。下面,我会对这些方法进行一些展开。
Ⅰ、调用System.exit()函数的方法
可以从API上直观的看到,该方法用于终止虚拟机,而当虚拟机被终止了,程序运行也就结束了。
下面时简单的代码演示:
可以看到finally语句块中的输出finally语句块被执行没有执行,而当我们注释掉语句Ststem.exit(1);语句后,finally中的语句块被执行了(后续不会再进行注释操作,第一次做注释操作便于前后做参照,表明确实是Ststem.exit(1);语句生效导致finally语句块不输出)。
Ⅱ、调用halt()函数的方法
同样我们可以从API上找到相关方法,它隶属于Runtime类。可以看到它的使用与Ststem.exit()方法的使用是一样的,都是强制终止当前运行Java虚拟机。
下面做代码演示:
作用与使用方法上看,Ststem.exit()与Runtime.getRuntimr.halt()方法没有太大差别(区别涉及线程)。
Ⅲ、try语句块循环溢出
正如字面意思,try语句块陷入死循环导致虚拟机终止程序将不会执行finally语句块中的内容。
下面做代码演示:
Ⅳ、守护线程
如果守护线程刚开始执行到finally代码块,此时没有任何其他非守护线程,那么虚拟机将退出,此时 JVM 不会等待守护线程的 finally 代码块执行完成。(涉及线程【线程中守护线程生命周期与主线程一起一样,主线程结束,守护线程即便有未执行的finally依然会强制结束】)
二、finally代码执行时遇到的问题。
使用finally语句块时遇到的问题基本可以分为两类,一类是上述的不执行问题,另一类则是执行时finally语句块与return返回值之间的问题(不涉及返回值与强制终止的finally语句块将必定被执行,必定被执行,必定被执行)。
而finally语句块与return之间的情景又大致分为以下两种情况:try或catch语句存在返回值,而finally语句块有返回值或抛出异常语句;try或catch语句存在返回值,而finally语句块无返回值但对返回的值进行修改。我将对其做简单的分析。
Ⅰ、finally语句块有返回值或抛出异常语句
无视前面try和catch语句中的返回值,执行finally中的返回值或将异常抛出。
Ⅱ、finally语句块无返回值但对返回的值进行修改
首先,我们要对return和finally执行次序进行探讨。毫无疑问,try中的return先执行,但他并没有直接将值返回给函数,而是将值备份后,再执行完finally语句块才会返回给函数。而在值备份后到程序结束前的这段时间里,finally语句块可能会对返回的值造成影响。
这又分作两种情况:1.返回值是引用类型;2.返回值是数据类型(返回值无影响但与数据类型做区分)。下面我会对上面两种情况进行展开。
1.返回值是引用类型
当返回值是引用类型时,return备份的值是引用类型对象指向的地址,finally语句块对其对象进行操作时会改变指向的地址中内存的值,从而使得结果返回值与只执行try语句时的返回值不太一样。
下面做代码演示:
可以看到返回的对象t中存放的a的值并不是try中return方法备份的,t对象中a的值可以被finally方法修改。
2.返回值是数据类型
try语句块中return备份的值即为那个数据的具体数值,finally语句对其进行改变时不会造成影响,这里独立列出来主要是和引用数据区分。
下面做代码演示:
可以看到虽然finally语句块中对返回值a进行了操作,但必不能影响返回值。
结语
本人第一次做技术博客,不足之处望批评指正。
参考文章:
https://juejin.im/post/5e1346ba5188253a6b371d78?utm_source=gold_browser_extension#heading-12
工具:JDK11API