final、finally、finalize作用分别是什么,有没有关系?

24 篇文章 0 订阅
3 篇文章 0 订阅

一、final

final之前有讲过,如果从入门级了解final请参考另一篇博客final,权限,内部类,引用类型
谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字。另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法。

在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。

1.final修饰的类不能被继承

当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。

2.final修饰的方法不可以被重写

如果 final 修饰方法,那么该方法就是最终方法不能被重写,如果一个类被final修饰了,那他没有任何子类,说白了就是最终类可能被继承再有子类
使用final方法的原因有二:

  • 第一、把方法锁定,防止任何继承类修改它的意义和实现。
  • 第二、高效。编译器在遇到调用final方法时候会转入内嵌机制,大大提高执行效率。

3.final修饰的变量不可以第二次被赋值

  • 修饰的变量可以先声明,后赋值。
  • 对于基本类型来说,不可改变就是不能改变他的数据值,
  • 对于引用数据类型来说,不能改变数据类型的内存地址,也就是说在对其初始化之后便不能再让其指向另一个对象。
  • 对于成员变量来说,如果使用final修饰,必须立马初始化而且这个成员变量不能生成set方法

注:类的private方法会隐式地被指定为final方法。

4. -final修饰的所有常量字母都要大写

在这里插入图片描述

5.修饰的变量可以防止指令重排序

问:关于final修饰的变量可以防止指令重排序????
除了volatile之外,Java还有两个关键字能实现可见性,即synchronizedfinal。同步块的可见性是由“对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store、write操作)”这条规则获得的,而final关键字的可见性是指:被final修饰的字段在构造器中一旦初始化完成,并且构造器没有把“this”的引用传递出去(this引用逃逸是一件很危险的事情,其他线程有可能通过这个引用访问到“初始化了一半”的对象),那在其他线程中就能看见final字段的值。

二、finally

finally归类为异常处理一块,详情介绍看另一篇博客java异常体系简介
finally作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下。

  • 只有与finally对应的try语句块得到执行的情况下,finally语句块才会执行。

有两种特殊情况可以看看

  • try 语句块中执行了 System.exit (0) 语句,终止了 Java 虚拟机的运行,finally 语句块就不会执行
  • 当一个线程在执行 try 语句块或者 catch 语句块时被打断(interrupted)或者被终止(killed),与其相对应的 finally 语句块可能不会执行,还有更极端的情况,就是在线程运行 try 语句块或者 catch语句块时,突然死机或者断电,finally 语句块肯定不会执行了。哈哈哈

三、finalize

这里可以先了解一下gc和java四大引用深入理解Java中的四种引用类型:强引用、软引用、弱引用和虚引用(附全代码解析)
finalize()是在java.lang.Object里定义的,也就是说每一个对象都有这么个方法。这个方法在gc启动,该对象被回收的时候被调用。其实gc可以回收大部分的对象(凡是new出来的对象,gc都能搞定,一般情况下我们又不会用new以外的方式去创建对象),所以一般是不需要程序员去实现finalize的。
特殊情况下,需要程序员实现finalize,当对象被回收的时候释放一些资源,比如:一个socket链接,在对象初始化时创建,整个生命周期内有效,那么就需要实现finalize,关闭这个链接。
  使用finalize还需要注意一个事,调用super.finalize();

一个对象的finalize()方法只会被调用一次,而且finalize()被调用不意味着gc会立即回收该对象,所以有可能调用finalize()后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会调用finalize(),产生问题。 所以,推荐不要使用finalize()方法,它跟析构函数不一样。

源码中这样写到
在这里插入图片描述

gc时对象自救方式

当对象变成可达性分析算法中判定为不可达的对象,也不是“非死不可”的,这时候它们暂时还处于“缓 刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程:如果对象在进行可达性分析后发现没 有与GC Roots相连接的引用链,那它将会被第一次标记,随后进行一次筛选,筛选的条件是此对象是 否有必要执行finalize()方法。假如对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用 过,那么虚拟机将这两种情况都视为“没有必要执行。

如果这个对象被判定为确有必要执行finalize()方法,那么该对象将会被放置在一个名为F-Queue的 队列之中,并在稍后由一条由虚拟机自动建立的、低调度优先级Finalizer线程去执行它们的finalize() 方法。这里所说的“执行”是指虚拟机会触发这个方法开始运行,但并不承诺一定会等待它运行结束。 这样做的原因是,如果某个对象的finalize()方法执行缓慢,或者更极端地发生了死循环,将很可能导致F-Queue队列中的其他对象永久处于等待,甚至导致整个内存回收子系统的崩溃。finalize()方法是对象逃脱死亡命运的最后一次机会,稍后收集器将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己 (this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移出“即将回收”的集 合;如果对象这时候还没有逃脱,那基本上它就真的要被回收了。
一次对象自我拯救的演示

package com.mystep.step.demo1;

/**
 * @author zxj
 * @date 2021年08月15日 17:29
 */
public class FinalizeEscapeGC {
    public static FinalizeEscapeGC SAVE_HOOK = null;

    public void isAlive() {
        System.out.println("yes, 我还活着:)");
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize 方法执行!");
        FinalizeEscapeGC.SAVE_HOOK = this;
    }

    public static void main(String[] args) throws Throwable {
        SAVE_HOOK = new FinalizeEscapeGC(); //对象第一次成功拯救自己
        SAVE_HOOK = null;
        System.gc(); // 因为Finalizer方法优先级很低,暂停0.5秒,以等待它
        Thread.sleep(500);
        if (SAVE_HOOK != null) {
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("no, 我死了 :(");
        }// 下面这段代码与上面的完全相同,但是这次自救却失败了
        SAVE_HOOK = null;
        System.gc(); // 因为Finalizer方法优先级很低,暂停0.5秒,以等待它
        Thread.sleep(500);
        if (SAVE_HOOK != null) {
            SAVE_HOOK.isAlive();
        } else {
            System.out.println("no, 我死了:(");
        }
    }
}

打印结果
在这里插入图片描述

SAVE_HOOK对象的finalize()方法确实被垃圾收集器触发 过,并且在被收集前成功逃脱了。
另外一个值得注意的地方就是,代码中有两段完全一样的代码片段,执行结果却是一次逃脱成 功,一次失败了。这是因为任何一个对象的finalize()方法都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()方法不会被再次执行,因此第二段代码的自救行动失败了。
建议大家尽量避免使用它,如今已被官方明确声明为 不推荐使用的语法。finalize()能做的所有工作,使用try-finally或者其他方式都可以做得更好、 更及时,所以建议大家完全可以忘掉Java语言里面的这个方法。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值