final、finally、finalize的区别

​1、final、finally、finalize的区别

  º  final修饰类,类不可继承。修饰方法,方法不可重写。修饰变量,变量不可修改。

    java.lang包下很多类被声明成final,在第三方类库的一些基础类也同样如此。可有效避免API使用者更改基础功能,是某种程度上保证平台安全的必要手段。

    final可保护只读数据,在并发编程中,明确声明不能再赋值的final变量有利于减少额外的同步开销,也可省去一些防御性拷贝的必要。

    final可能有助于JVM将方法进行内联,可能改善编译器进行条件编译的能力等。(final对性能的影响一般都没必要考虑)

  º  finally保证Java某段代码一定被执行,可用来关闭JDBC连接、unlock锁等。如果要关闭资源,推荐Java7新加的try-with-resources语句,通常Java能够更好地处理异常情况,编码量也会少很多。

  º  finalize是Object的方法,设计的目的是保证对象被垃圾收集前完成特定资源的回收,finalize不推荐使用了,在Java9开始被标记为@Deprecated

    一般不要实现finalize方法,也不要指望用它进行资源回收。因为我们无法保证finalize方法什么时候执行,执行的是否符合预期。使用不当会影响性能,导致死锁、挂起等。

    利用try-with-resourcestry-finally是非常好的回收资源方法。如果确实要额外处理,可以考虑Java提供的Cleaner机制或其他替代方法。

2、final不是immutable,参考如下代码

    final能约束strList的引用不被改变,但strList还是可以add元素。如果希望对象本身不可变,需要相应的类支持不可变的行为。如:List.of(E... elements)返回包含任意数量元素的不可变列表(List.of()是Java9新加的方法)。示例代码中那句add会在运行时抛异常。

    Immutable在很多场景下是适用的,Java没有原生的不可变支持,要类实现Immutable需做到:

Ⅰ  class自身声明final,不允许别人拓展。

 所有成员变量privatefinal修饰,并不提供setter方法。

 创建对象用深拷贝,而不是赋值。

 需要才提供getter方法,使用copy-on-writer原则(先备份原来的值,在修改)。

3、Lamdba引用外部局部变量用final修饰的原因

    方法执行时会在线程栈中创建一个栈帧,栈帧中包含局部变量表、操作数栈、动态连接等。Lamdba表达式是匿名内部类实现接口的函数,在执行函数时要在线程栈中创建一个新的栈帧,不同栈帧是不能共享局部变量表的。Lamdba表达式栈帧中的变量其实是外部方法栈帧的一个私有拷贝。为保证程序正确性,要求被引用的局部变量被定义成final。如果不定义成final可能会出现数据不一致的情况。

4、finalize真的那么不堪吗?

    finalize已被业界证明是非常不好的实践。finalize执行和垃圾收集关联,实现finalize会导致相应对象回收呈数量级上的变慢,大概是40~50倍。

    finalize在对象被垃圾收集前调用,JVM要对它进行额外处理,这会成为快速回收的阻碍(可能导致对象经过多个垃圾收集周期才被回收)。finalize拖慢垃圾收集,导致大量对象堆积,也是导致OOM的原因之一。System.runFinalization()让JVM积极一点也许有用,但这是不可预测,不能保证的。

    垃圾收集时间的不可预测,可能会极大加剧资源占用。对消耗非常高频的资源,不应指望finalize去做资源释放的主要职责,最多让finalize作最后的盾牌(资源用完应显式释放,或利用资源池来尽量重用)。

5、finalize为什么不可预测

    runFinalizer中的Throwable被生吞了,这意味着出现异常或错误,我们得不到任何有效信息。而且Java在finalize阶段也没有好的方式处理任何信息,更加不可预测。

6、有什么机制可替换finalize吗?

    Java在逐步使用java.lang.ref.Cleaner替换原有的finalize实现。

    Cleaner利用了幻象引用(Phantom Reference),幻象引用是种常见的事后处理机制。利用幻象引用和引用队列,可保证对象被彻底销毁前做些类似资源回收的工作。如:关闭文件描述符(操作系统有限的资源)。

    Cleaner比finalize更轻量、更可靠。Cleaner是独立的,有自己的运行线程,可避免意外死锁等问题。

    从可预测性来说,Cleaner或幻象引用仍然有限,如果幻象引用堆积也会出现问题,所以Cleaner适合作最后的手段,而不完全依赖Cleaner进行资源回收。

    很多第三方库利用幻象引用定制资源收集,如:MySQL-JDBC-Driver中的mysql-connector-j就利用了幻象引用机制。

    幻象引用可进行链条式依赖关系的动作,如进行总量控制的场景,保证只有连接被关闭,相应资源被回收,连接池才能创建新的连接。

    幻象引用代码不慎添加了对资源的强引用关系会导致循环引用关系,MySQL-JDBC在特定模式会有这种问题导致内存泄漏。幻象机制应避免普通的内部类隐含对外部对象的强引用,那会使外部对象无法进入幻象可达的状态。

 

 

 

往期精彩文章:
单例设计模式之readResolve()方法
Exception和Error的区别
MySQL基本架构与一条SQL语句的执行流程

更多分享,可关注公众号「 桂圆金宝宝 」。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值