第3讲 谈谈final、finally、 finalize有什么不同?

谈谈 final、finally、 finalize 有什么不同?

典型回答
final 可以用来修饰类、方法、变量,分别有不同的意义,final 修饰的 class 代表不可以继承扩展,final 的变量是不可以修改的,而 final 的方法也是不可以重写的(override)。
finally 则是 Java 保证重点代码一定要被执行的一种机制。我们可以使用 try-finally 或者 try-catch-finally 来进行类似关闭 JDBC 连接、保证 unlock 锁等动作。
finalize 是基础类 java.lang.Object 的一个方法,它的设计目的是保证对象在被垃圾收集前完成特定资源的回收。finalize 机制现在已经不推荐使用,并且在 JDK 9 开始被标记为 deprecated。

1.final 字段对性能的影响,大部分情况下,并没有考虑的必要。

2. finalize 被设计成在对象被垃圾收集前调用,要明确它是不推荐使用的,业界实践一再证明它不是个好的办法。
在 Java 9 中,甚至明确将 Object.finalize() 标记为 deprecated!如果没有特别的原因,不要实现 finalize 方法,也不要指望利用它来进行资源回收
一旦实现了非空的 finalize 方法,就会导致相应对象回收呈现数量级上的变慢,有人专门做过 benchmark,大概是 40~50 倍的下降。

3. Cleaner
Java 平台目前在逐步使用 java.lang.ref.Cleaner 来替换掉原有的 finalize 实现。Cleaner 的实现利用了幻象引用(PhantomReference),这是一种常见的所谓 post-mortem 清理机制

利用幻象引用引用队列,可以保证对象被彻底销毁前做一些类似资源回收的工作,比如关闭文件描述符(操作系统有限的资源),它比 finalize 更加轻量、更加可靠。

吸取了 finalize 里的教训,每个 Cleaner 的操作都是独立的,它有自己的运行线程,所以可以避免意外死锁等问题。

JDK 自身使用的 Cleaner 机制仍然是有缺陷的,从可预测性的角度来判断,如果由于种种原因导致幻象引用堆积,同样会出现问题。

4. 为自己的模块构建一个 Cleaner

public class CleaningExample implements AutoCloseable {
        // A cleaner, preferably one shared within a library
        private static final Cleaner cleaner = <cleaner>;
        // 将 State 定义为 static,就是为了避免普通的内部类隐含着对外部对象的强引用,
        // 因为那样会使外部对象无法进入幻象可达的状态。
        static class State implements Runnable {
            State(...) {
                // initialize State needed for cleaning action
            }
            public void run() {
                // cleanup action accessing State, executed at most once
            }
        }
        private final State;
        private final Cleaner.Cleanable cleanable
        public CleaningExample() {
            this.state = new State(...);
            this.cleanable = cleaner.register(this, state);
        }
        public void close() {
            cleanable.clean();
        }
    }

5. final 不是 immutable!

// final 只能约束 strList 这个引用不可以被赋值
final List<String> strList = new ArrayList<>();
// 还是可以添加元素的
strList.add("Hello");
strList.add("world");
// List.of 方法创建的本身就是不可变 List
List<String> unmodifiableStrList = List.of("hello", "world");
// add 是会在运行时抛出异常的
unmodifiableStrList.add("again");

6. 实现 immutable 的类

  • 将 class 自身声明为 final,这样别人就不能扩展来绕过限制了。
  • 将所有成员变量定义为 private 和 final,并且不要实现 setter 方法
  • 通常构造对象时,成员变量使用深拷贝来初始化,而不是直接赋值,这是一种防御措施,因为你无法确定输入对象不被其他人修改。
  • 如果确实需要实现 getter 方法,或者其他可能会返回内部状态的方法,使用 copy-on-write 原则,创建私有的 copy。

扩展:
阅读相关第三方库直接利用幻象引用定制资源收集

比如广泛使用的 MySQL JDBC driver 之一的 mysql-connector-j,就利用了幻象引用机制。幻象引用也可以进行类似链条式依赖关系的动作,比如,进行总量控制的场景,保证只有连接被关闭,相应资源被回收,连接池才能创建新的连接。

阅读相关第三方库自己直接利用幻象引用定制资源收集
比如广泛使用的 MySQL JDBC driver 之一的 mysql-connector-j,就利用了幻象引用机制。幻象引用也可以进行类似链条式依赖关系的动作,比如,进行总量控制的场景,保证只有连接被关闭,相应资源被回收,连接池才能创建新的连接。


笔记整理自极客时间《Java核心技术36讲》专栏,仅做学习用途。
《Java核心技术36讲》 :前Oracle首席工程师,讲解Java核心技术原理 、 Java面试必考知识点 、 完整的Java知识体系 ,有深度有广度。
极客时间专栏《Java核心技术36讲》

发布者

jahentao

挖掘概念,创造工具