Java核心 -- final + finally + finalize
final
- 修饰类,代表不可以继承扩展
- 修饰变量,代表变量不可以修改
- 修饰方法,代表方法不可以重写
实践
- 推荐使用
final
关键字来明确表示代码的语义和逻辑意图 - 将方法或类声明为
final
,明确表示不允许重写或继承 - 使用
final
修饰参数或变量,能够避免意外赋值而导致的编程错误 final
变量产生了某种程度的不可变(immutable)的效果,可以用于保护只读数据- 现在
JVM
足够智能,_**final
对性能的影响,在大部分情况下,都没有必要考虑**_,应用程序更应该关注的是语义
final != immutable
Java目前没有原生的immutable支持
1 | // final只能约束strList这个引用不可以被赋值,但strList对象本身的行为是不受影响的 |
immutable类
final class
- 所以成员变量定义为
private final
,并且不要实现setter
方法 - 构造对象时,成员变量使用深度拷贝来初始化
- 防御编程:因为无法确保输入对象不会被其它线程修改
- 如果需要实现
getter
,使用copy-on-write
原则
finally
- 保证重点代码一定要被执行的一种机制
try-finally
和try-catch-finally
try-with-resources
(JDK 7引入)
1 | try { |
finalize
java.lang.Object
中的一个protected
方法- 设计目标:_保证对象在被GC前完成特定资源的回收_
- 不推荐使用,在
JDK 9
中已经被标记为@Deprecated(since="9")
- 无法保证
finalize()
何时会执行,执行的结果是否符合预期- 如果使用不当会影响性能,导致程序死锁、挂起等问题
- 一旦实现类非空的
finalize
方法,会导致对象回收呈现数量级上的变慢(40~50倍) - 实现了
finalize
方法的对象是特殊公民,JVM需要对它们进行额外的处理finalize
本质上成为了快速回收的阻碍者- 可能导致对象经过多个GC周期才能被回收
System.runFinalization()
同样是不可预测的- 实践中,
finalize
会拖慢GC,导致大量对象堆积,有可能导致OOM
- 对于消耗非常高频的资源,不要指望
finalize
去承担释放资源的主要职责- 推荐做法:资源用完即显式释放,或者利用资源池来复用
- 另外,
finalize
会掩盖资源回收时的出错信息
1 | // Throwable被生吞 |
替代方案 – Cleaner
- Java平台逐渐使用
java.lang.ref.Cleaner
替换掉原有的finalize
实现 Cleaner
的实现利用了幻象引用(Phantom Reference)- 利用幻象引用和引用队列,保证对象被销毁之前做一些类似资源回收的工作
Cleaner
比finalize
更加轻量,更加可靠- 每个
Cleaner
的操作都是独立的,都有自己的运行线程,可以避免意外死锁等问题 - 从可预测的角度来判断,
Cleaner
或者幻象引用改善的程度依然是有限的- 由于种种原因导致幻象引用堆积,同样会出现问题
Cleaner
适合作为最后的保证手段,而不能完全依赖Cleaner
进行资源回收
1 | public class CleaningExample implements AutoCloseable { |
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.