final、finally 和 finalize

一、final 的三种用法

1️⃣如果一个类被声明为 final,意味着它不能再派生出新的子类,即不能被继承,因此它和 abstract 是反义词。想要让一个类永远不被继承,就可以用 final 修饰,但要注意:final 类中所有的成员方法都会隐式的定义为 final 方法。

2️⃣将变量声明为 final,可以保证它们在使用中不被改变,被声明为 final 的变量必须在声明时给定初值,在以后的引用中只能读取不可修改。

  1. 当 final 修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化。
  2. 当 final 修饰一个引用类型时,则在初始化之后便不能再让其指向其它对象了,但该引用所指向的对象的内容是可以发生变化的。本质上是一回事,因为引用的值是一个地址,final 要求该值,即地址的值不发生变化。
  3. final 修饰一个成员变量(属性),必须要显示初始化。这里有两种初始化方式,一种是在变量声明的时候初始化;第二种方法是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。
  4. 当函数的参数类型声明为 final 时,说明该参数是只读型的,无法改变该参数的值。
    注意:
    Java 中,String 被设计成 final,平时使用时,为什么可以改变 String 的值?
    字符串常量池是 Java 堆内存中一个特殊的存储区域。当建立一个 String 对象时,假设常量池不存在该字符串,则创建一个,若存在则直接引用已经存在的字符串。当改变 String 对象值的时候,例如String a = "A"; a = "B";,a 是 String 对象的一个引用(这里所说的 String 对象其实是指字符串常量),当执行 a = “B” 时,并不是原本 String 对象(“A”)发生改变,而是创建了一个新的对象(“B”),令 a 引用它。

3️⃣被声明为 final 的方法也同样只能使用,不能在子类中被重写。使用 final 方法的原因主要有两个:

  1. 把方法锁定,以防止继承类对其进行更改。
  2. 效率。在早期Java版本中,会将 final 方法转为内嵌调用。但若方法过于庞大,可能在性能上不会有多大提升。因此在最近版本中,不需要 final 方法进行这些优化了。

注意:若父类中 final 方法的访问权限为 private,将导致子类中不能直接继承该方法。因此,可以在子类中定义相同方法名的函数,此时不会与重写 final 矛盾,而是在子类中重新定义了新方法。

class A{
  private final void getName(){}
}
public class B extends A{
  public void getName(){}
  public static void main(String[]args){
     System.out.println("OK");
  }
}

二、finally

finally 作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(无论是否抛出异常,只要JVM不关闭,与 finally 对应的 try 语句块得到执行的情况下,都能执行)。经常被用在需要释放外部资源的情况下。

finally 语句块没有执行的情况:

  1. 在 try 块中,发生异常代码前执行System.exit(0)语句,终止 JVM 的运行。
  2. 发生异常后,在 catch 块中执行 System.exit(0) 语句,终止 JVM 的运行。
  3. 当一个线程在执行 try 块或者 catch 块时被打断(interrupted)或者被终止(killed),与其相对应的 finally 块可能不会执行。
  4. 极端情况,就是在线程运行 try 块或者 catch 块时,突然死机或者断电,finally 块肯定不会执行了。

三、finalize

finalize() 是在java.lang.Object里定义的,也就是说每个对象都有该方法。Java 中允许使用 finalize() 在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。该方法是由垃圾收集器在销毁对象时调用的,通过重写 finalize() 可以整理系统资源或者执行其他清理工作。这个方法在 GC 启动,该对象被回收的时候被调用。其实 GC 可以回收大部分的对象(凡是 new 出来的对象,都能搞定,一般情况下又不会用 new 以外的方式去创建对象),所以一般不需要重写 finalize()。特殊情况,当对象被回收的时候释放一些资源,比如:一个 socket 链接,在对象初始化时创建,整个生命周期内有效,那么就需要重写 finalize,关闭这个链接。
使用 finalize 还需要注意一个事,调用 super.finalize();
一个对象的 finalize() 只会被调用一次,而且 finalize() 被调用不意味着 GC 会立即回收该对象。有可能调用 finalize() 后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会调用 finalize(),产生问题。 所以,不推荐使用 finalize(),它跟析构函数不一样。

四、面试

  1. int x 被修饰成 final,意味着 x 不能在 addOne() 中被修改。所以编译报错。

  2. 判断

@Data
public class Tba {
  private int a;
}
public class Wg {
    public static void main(String[] args) {
        Tba tba = new Tba();
        System.out.println(new Wg().addOne(tba));
    }
    public int addOne(final Tba tba) {
        int a = tba.getA();
        return ++a;
    }
}

答案: 正确。在 addOne() 中,参数 tba 被修饰成 final。如果在 addOne() 里修改了 tba 的引用(tba = new Tba()),那么如同上例这题也是错的 😉。但这里修改的是 tba 的 member vairable(成员变量),而 tba 的 reference 并没有改变。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JFS_Study

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值