final、finally、finalize区别

final、finally、finalize区别

final

final 关键字可以对类、方法、变量进行修饰。对类进行修饰,表示该类不能继承;对方法进行修饰,表示该方法不能被重写;对变量进行修饰,表示该变量不可变(引用变量不一样)。

  • final修饰的类,可以防止API 使用者更改基础功能,比如 String 类是不可变的,如果String不用final修饰,可能就会出现意料之外的错误,这样在某些程度上,可以保证平台的安全。
  • 使用final修饰参数和变量,让数据不可变,避免后序因为改变数值而造成程序错误,有时,我们建议将所有方法参数、本地变量、成员变量声明成 final 。
  • final使数据不可变,所以保证了数据的安全性,在并发的情况下,有利于减少同步开发的消耗, 省去一些防御性拷贝的必要 。

有些书籍说明,final可能有助于 JVM 将方法进行内联,可以改善编译器进行条件编译的能力等,能够提高 jvm 性能。

特殊情况

final并不等于 Immutable

final List<String> list = new ArrayList<>();
list.add(" HELLO ");
list.add(" WORLD ");
list.forEach(System.out::println);

对于引用对象来说,final 只是保证引用不能改变,即不能改变指针,我们还是可以对位于堆内的对象的行为是不受限制的。所以上面的代码是能够正常运行的。

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

copy-on-write 目的是保证 只有一个专门的线程往里面写;可以有多个线程读取,这样能保证线程同步; 每次getter访问时,都只会访问到拷贝引用,而不是原始数据引用,这样子就保证getter拿到的东西不会直接被外部修改 。

所以一般不建议使用IDE一键生成 getter/setter。

为什么匿名内部类,访问局部变量时,局部变量要用final来修饰?

因为匿名内部类访问局部变量的时候,会复制变量,对副本进行操作,可以防止数据一致性问题。

finally

finally 保证代码必定被执行(几种情况除外)的机制,比如,使用 try-finally 或者 try-catch-finally 来进行类似关闭 JDBC 连接、保证 unlock 锁等动作 。

如果需要关闭连接资源等,建议使用 JDK7 及以后的 try-with-resources ,try-with-resources 能够帮我自动关闭实现了 AutoCloseable 接口的类。

try{
    System.exit(0);
} catch (Exception e) {
    e.printStackTrace();
}finally {
    System.out.println("finally method");
}

上述代码就是特殊情况之一,finally 并不会执行,不会输出finally method,因为执行System.exit(0);后,程序已经停止运行了。try 中含有 return语句,finally照样执行。

还有两种情况:

  • try 语句中含有死循环;
  • 当前线程被杀死,finally 中的代码照样不能执行。

finalize

finalize 已经明确被不推荐使用了,设计目的是保证对象在被垃圾收集前完成特定资源的回收, JDK 9 开始被标记为 deprecated。

在可达性分析算法中,宣告一个对象正真死亡至少需要两次标记,如果 没有发现与 GC Roots 连接的引用链,这是对象会被第一次标记,进行一次筛选,看看对象是否有必要执行 finalize() 方法,(如果对象没有覆盖finalize() 方法,或者虚拟机已经调用过 finalize() 方法,jvm 认为没有必要执行finalize() 方法)。

如果有必要执行finalize() 方法,则会将 对象放入F-Queue 队列中,稍后虚拟机自动创建一个低调度优先级的线程去执行队列中对象的finalize() 方法(这里说的执行finalize() ,只是触发 finalize() 方法,并不意味着 finalize() 方法一定要执行完毕,所以如果某一个对象的 finalize() 方法执行缓慢或 陷入死循环,可能造成其他对象永久等待,甚至使内存陷入崩溃)。

第二次标记,如果在收集器对队列中的对象进行标记的时候,有对象重新建立连接,就可以避免被回收,那么该对象将被移除队列,如果没有逃脱,必定会被回收。

所以对象的finalize() 方法执行,它依然可以存活。

你无法保证 finalize 什么时候执行,执行的是否符合预期。使用不当会影响性能,导致程序死锁、挂起等。

我们完全可以使用 try-with-resources 或者 try-finally 机制 替代 finalize() 。

Java 平台目前在逐步使用 java.lang.ref.Cleaner 来替换掉原有的 finalize 实现。

总结

  • final
    修饰类:不可被继承
    修饰方法:不可重写
    修饰变量:不可修改,只能约束引用不可以被再次赋值。匿名内部类访问局部变量时需要使用 final,因为 Innerclass 实际会 copy 一份局部变量,final 可以防止出现数据一致性问题;

  • finally:Java 保证重点代码一定要被执行的机制,try - finally,除非在 finally 前执行了 System.exit(1)、try 中死循环、线程被杀死;

  • finalize:基础类 Object 的一个方法,保证对象在被垃圾收集前完成特定的资源回收。由于 finalize 执行时间不确定且可能造成程序死锁、拖慢垃圾收集等问题,Java 9 中将改方法废弃
    优化:使用 Cleaner 配合幻想引用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值