声明

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

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

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

提问

  • 谈谈 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),如果你觉得本篇内容有帮助到你,可以转载但记得要关注,要标明原文哦,谢谢支持~

谈谈 final finally finalize 区别的更多相关文章

  1. Reader和Writer区别final.finally.finalize区别

    Reader和Writer是字符操作流,Writer是输出的,而Reader是输入的. 首先找到一个文件,比如:File file=new File("."+File.separa ...

  2. 谈谈final, finally, finalize的区别

    final 修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承.因此一个类不能既被声明为 abstract的,又被声明为final的.将变量或方法声明为fi ...

  3. final finally finalize 区别及用法

    final 1,final修饰的class,代表不可以继承扩展. 2.final的方法也是不可以重写的. 3.final修饰的变量是不可以修改的.这里所谓的不可修改对于基本类型来来,的确是不可以修改. ...

  4. final finally finalize 区别

    public class Demo { public static void main(String[] args) { long start = System.currentTimeMillis() ...

  5. Final,finally,finalize区别

    final— 修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承.因此一个类不能既被声明为 abstract的,又被声明为final的.将变量或方法声明为f ...

  6. final,finally,finalize有什么区别?String, StringBuffer, StringBuilder有什么区别?Exception和Error有什么区别?

    继上篇JVM学习之后,后面将分三期深入介绍剩余JAVA基础面试题,每期3题. 题目一.final,finally,finalize有什么区别? /*请尊重作者劳动成果,转载请标明原文链接:*/ /* ...

  7. Android源码中final关键字的用法及final,finally,finalize的区别

    Android开发的学习流程 final,finally,finalize的区别 Android的发展越来越快,Android开发人员越来越多,当两种情况碰撞,在诸多开发者中跟紧Android步伐脱颖 ...

  8. 【Java面试题系列】:Java中final finally finalize的区别

    本篇为[Java面试题系列]第三篇,文中如有错误,欢迎指正. 第一篇链接:[Java面试题系列]:Java基础知识常见面试题汇总 第一篇 第二篇链接:[Java面试题系列]:Java基础知识常见面试题 ...

  9. java面试题(杨晓峰)---第三讲谈谈final、finally、finalize有什么不同?

    java语言有很多看起来相似,但用途却完全不相同的语言要素,这些内容往往容易成为面试官考察你知识掌握程度的切入点. 今天我要问你一个基础的java经典题目,谈谈final.finally.finali ...

随机推荐

  1. 为什么使用 Spring Boot?

    Spring 是一个非常流行的基于Java语言的开发框架,此框架用来构建web和企业应用程序.与许多其他仅关注一个领域的框架不同,Spring框架提供了广泛的功能,通过其组合项目满足现代业务需求. S ...

  2. Android Studio 3.0 变化之 implementation与compile

    Android Studio 3.0 出来很久了,本文就着重介绍一下 新版本中 Moudle 中 build.gradle 文件中的变化. 我们来看看新建一个项目在 Moudle 中的 depende ...

  3. Linux 下 安装Python第三方模块工具箱pip,以及用pip安装的方法

    下载文件 wget https://bootstrap.pypa.io/get-pip.py --no-check-certificate 执行安装 python get-pip.py 可以做一个软连 ...

  4. 用Ajax遍历三级下拉框

    用Ajax遍历三级下拉框 //通过一级分类的id查二级分类(记得在前端网页按钮绑定点击事件) function getSecondCategory(oneCategoryId){ alert(&quo ...

  5. 深入分析Java I/O的工作机制 (三)网络I/O的工作机制 很详细

    3.网络I/O的工作机制 前言:数据从一台主机(服务端)发送到网络中的另一台主机(客户端)需要经过很多步骤:首先需要有相互沟通的意向.其次要有能够沟通的物理渠道(物理链路):是通过电话,还是直接面对面 ...

  6. redis安装以及安全配置

    redis安装以及安全配置 1. 安装 sudo apt-get install redis-server 使用which查询redis执行体安装路径: which redis-server #/us ...

  7. 多线程 Thread.yield 方法到底有什么用?

    概念 我们知道 start() 方法是启动线程,让线程变成就绪状态等待 CPU 调度后执行. 那 yield() 方法是干什么用的呢?来看下源码. /** * A hint to the schedu ...

  8. 写了2年python,知道 if __name__ == '__main__' 什么意思吗?

    相信刚接触Python的你一定有过如此经历,把所有的代码都写在 if __name__ == '__main__'下,因为有人告诉你,这样比较符合 Pythonista 的代码风格. 殊不知这段代码的 ...

  9. 【ABP框架系列学习】N层架构(3)

    目录 0.引言 1.DDD分层 2.ABP应用构架模型 客户端应用程序(Client Applications) 表现层(Presentation Layer) 分布式服务层(Distributed ...

  10. redis 系列3 数据结构之简单动态字符串 SDS

    一.  SDS概述 Redis 没有直接使用C语言传统的字符串表示,而是自己构建了一种名为简单动态字符串(simple dynamic string, SDS)的抽象类型,并将SDS用作Redis的默 ...