谈谈 final finally finalize 区别

作者 : 开心源码 本文共2892个字,预计阅读时间需要8分钟 发布时间: 2022-05-11 共60人阅读

公告

本篇所涉及的提问,正文的知识点,全都来自于杨晓峰的《Java核心技术36讲》,当然,我并不会全文照搬过来,毕竟这是付费的课程,应该会涉及到侵权之类的问题。

所以,本篇正文中的知识点,是我从课程中将知识点耗费后,使用个人的了解、观念所表达出来的文字,参考了原文,但因为是个人了解,因而不保证观点完全正确,也不代表错误的观点是课程所表达的。假如这样依旧还是侵权了,请告知,会将发表的文章删掉。

当然,假如你对此课程有兴趣,建议你自己也购买一下,新使用户立减 30,微信扫码订阅时还能返现 6 元,相当于 32 元购买 36 讲的文章,每篇文章还不到 1 元,蛮划算的了。

QQ图片20180703142535.png

提问

  • 谈谈 final、finally、finalize 有什么不同?
  • 为什么局部内部类和匿名内部类只可以访问局部final变量?

正文

emmm,说实话,感觉这一讲如同没什么实质性内容。而且,就像评论区里有人提到的,搞不懂为啥总有人喜欢拿这三者来比较,它们有个毛的关系?仅仅就是由于单词相近就拿来比较?

但课程小结还是要做下,梳理梳理下相关面试知识点也好,那也不说废话了,结合原文和评论区,以及少量扩展,尽量多总结少量知识点吧。

final

final 是 java 中的关键字,可使用于修饰类,方法,变量。

当修饰类时,表明这个类不可被继承。Java 中有少量核心类都被 final 修饰了,比方 String,System。当考虑到安全性起因时,能将该类设计成 final。

当修饰方法时,表明该方法不可被重写。一般是某些流程控制不希望被修改掉时,能将这些方法公告成 final,比方 View 中的 measure()requestFocus()findViewById()

当修饰变量时,表明该变量为常量,不允许被重新赋值,因而公告成 final 的变量都需要显示的进行赋值,否则编译会报错。

finally

finally 是确保 try-catch 方式最后执行的一种机制,通常的使用法都是在 finally 里进行少量资源的关闭,回收。比方 IO 流的关闭等等。

建议最好不要利使用 finally 来控制流程,也不要在 finally 中有返回值,否则很容易影响正常流程,导致流程结构特别杂乱。

另外,有些特殊情况下,finally 中的代码并不会被执行到,比方:

//1.try-catch异常退出try {    System.exit(1)} catch {    ....} finally {    //不会执行到这里    Log.d("finally", "finally");}//2.无限循环try {    while(true) {        ...    }} finally {    //不会执行到这里    Log.d("finally", "finally");}//3. 线程被杀死//当执行 try-catch 时,线程被杀死了,那么 finally 里的代码也无法被执行到

总之,finally 通常情况下都会最后被执行到,所以最好不要在这里有 return 之类的语句来影响正常流程。但在某些特殊的场景下,finally 并不会被执行到,理解一下就可。

finalize

这个是 Object 中的一个方法,方法注释说了很多,大概就是讲这个方法是由垃圾收集器即将要回收该对象时会调使用该方法,使用户可在这里做少量最后的资源释放工作。

以上是概念定义,但说实话,没使用过该方法,而且作者也说了,不推荐用 finalize 机制来做资源回收,并且在 JDK 9,这个方法已经被标志为 deprecated 废弃的方法了。

作者有提到说,由于我们无法保证 finalize 什么时候执行,执行能否符合预期,用不当还会影响性可以,导致程序死锁、挂起等问题。

那么,有其余方案来替代 finalize 解决回收资源的工作么?有,Cleaner 机制,这个我没接触过,作者提了这个替代方案。另外,作者也说了,回收资源最好就是资源使用完后就随手清理,或者者结合 try-catch-finally 机制回收。不论是 finalize 或者者 Cleaner 机制,最好都只将它看成是最后一道防线,一旦将主要的回收工作依赖于这两个机制的话,很容易出现各种问题。

扩展

为什么局部内部类和匿名内部类只可以访问局部final变量?

先来看这么段代码:

//参数 msg 必需公告为 final 类型public void notifyChange(final String msg) {    mTextView.post(new Runnable() {        @Override        public void run() {            mTextView.setText(msg);        }    })}

在这里,post() 方法的参数是一个匿名内部类,在内部类中假如要用外部 notifyChange() 方法的参数 msg,那么必需将 msg 类型公告成 final,否则编译器会保错。

这种场景非常常见的吧,不论是相似上述的 Ui 场景,还有网络访问时也经常需要通过回调通知上层,此时也就经常出现这种场景了。

那么,有考虑过,为什么内部类只可以访问局部 final 变量么?

假如懂得反编译 class 文件的,那么应该就很清楚了。我也不懂,了解这点是通过阅读其余大神分析的文章,以下是我的了解:

首先,变量都是有生命周期的,成员变量的生命周期就跟随着对象的整个生命周期,而局部变量的生命周期则是非常有限。

比方方法内部的局部变量,它的生命周期就是在这个方法执行结束就终止。同样,方法的参数也是局部变量,它是生命周期也同样是到该方法执行结束。

另外,内部类的执行时机有时是会在外部方法执行结束之后。就拿上述例子来说,post() 中 Runnable 的执行时机,一定是在外部 notifyChange() 方法执行完之后的。

那么,问题来了。内部类 Runnable 的执行需要用到外部方法 notifyChange() 的参数,但当它执行的时候,这个参数的生命周期早已结束,已经被回收掉了。既然已经被回收了,内部类又是怎样用外部的这个局部变量呢?

有大神反编译了 class 文件后,给出了结论,原来内部类用外部的局部变量时,是通过 copy 一份过来。也就是说,其实内部类此时用的是自己内部定义的局部变量了,只是这个变量的值是复制外部那个局部变量的而已。

这也就解释了,为什么外部的局部变量明明已经被回收了,内部类依旧能用,由于内部类此时用的并不是外部类的局部变量引使用了。

但到这里,新的问题就来了:既然内部类用的局部变量本质上跟外部的局部变量是相互独立的两个变量,那假如在内部类中修改了这个局部变量的值会出现什么情况?是吧,数据的不一致性。

基于此,java 编译器就直接限定死,内部类用外部的局部变量时,必需将其限制为 final 类型,确保该变量不允许进行更改。

这样一来,其实也就顺便了解了,为什么成员变量能直接在内部类中用,由于成员变量的公告周期很长,不存在局部变量的问题。

以上内容,是在大神的文章里被醍醐灌顶了,感谢大神,原文链接放出来:

Java内部类详解


大家好,我是 dasu,欢迎关注我的公众号(dasuAndroidTv),假如你觉得本篇内容有帮助到你,能转载但记得要关注,要标明原文哦,谢谢支持~

dasuAndroidTv2.png

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 谈谈 final finally finalize 区别

发表回复