美文网首页
第3讲 | 谈谈final、finally、 finalize有

第3讲 | 谈谈final、finally、 finalize有

作者: governlee | 来源:发表于2019-04-16 23:05 被阅读0次

典型回答

final 可以用来修饰类、方法、变量,分别有不同的意义,final修饰class代表不可以继承扩展,final修饰的变量不可以修改,而final的方法不可以重写(override)

finally 则是 Java 保证重点代码一定要被执行的一种机制。我们可以使用 try-finally 或者 try-catch-finally 来进行类似关闭JDBC连接、unlock锁等动作。

finalize 是基础类 java.lang.Object的一个方法,它的设计目的是保证对象在被垃圾收集前完成特定资源的回收。finalize 机制现在已经不推荐使用,并且在 JDK 9开始被标记为deprecated。因为无法保证 finalize 什么时候执行,执行的是否符合预期,使用不当会影响性能,导致程序死锁、挂起等。

推荐使用 final 关键字来明确表示我们代码的语义、逻辑意图,这已经被证明在很多场景下是非常好的实践。

  • 我们可以将方法或者类声明为 final,这样就可以明确告知别人,这些行为是不许修改的。

  • 使用 final 修饰参数或者变量,也可以清楚地避免意外赋值导致的编程错误。

  • final 变量产生了某种程度的不可变(immutable)效果,可以用于保护只读数据,尤其是在并发编程中,因为明确地不能再赋值,能减少额外同步的开销,省去一些防御性拷贝。

final 并不等同于 immutable

// final 只能约束 strList 这个引用不可以被赋值,strList对象的行为不受影响
 final List<String> strList = new ArrayList<>();
 strList.add("Hello");
 strList.add("world");  
// java9 创建immutable list的方式
 List<String> unmodifiableStrList = List.of("hello", "world");
// 会抛出异常
 unmodifiableStrList.add("again");

Java 语言目前并没有原生的不可变支持,如果要实现 immutable,我们需要做到:

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

finalize 真的那么不堪?

finalize 的执行是和垃圾收集关联在一起的,一旦实现了非空的finalize方法,就会导致相应对象回收呈现数量级上的变慢,有人统计过benchmark,大概是40、50倍的下降。

finalize 被设计成在对象被垃圾收集前调用,本质上成为了快速回收的阻碍者,可能导致你的对象经过多个垃圾收集周期才被回收。实践中,因为 finalize 拖慢垃圾收集,导致大量对象堆积,也是OOM产生的原因之一。

从另一个角度,我们要确保回收资源就是因为资源都是有限的,垃圾收集时间的不可预测,可能会极大加剧资源占用。这意味着对于消耗非常高频的资源,千万不要指望 finalize去承担资源释放的主要职责。

资源用完即显式释放,或者利用资源池来尽量重用。

有什么机制可以替换 finalize 吗?

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

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

实践中,我们可以为自己的模块构建一个 Cleaner,然后实现自己的清理逻辑。下面是 JDK 自身提供的样例程序:

public class CleaningExample implements AutoCloseable {
        // A cleaner, preferably one shared within a library
        private static final Cleaner cleaner = <cleaner>;
        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();
        }
    }

相关文章

网友评论

      本文标题:第3讲 | 谈谈final、finally、 finalize有

      本文链接:https://www.haomeiwen.com/subject/vvkiwqtx.html