final关键字你真的会用?

时间:2022-07-23
本文章向大家介绍final关键字你真的会用?,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

背景

codeReview的时候,看到各种final关键字的”骚“操作,今天认真复习一下final关键字。 我们都知道final关键字可以使用到类上,也可以使用道方法上,也可以使用到数据上,但是他的使用会为我们提供哪些好处呢?

final关键自的使用

Java关键字final通常指的是“这是无法改变的”不想作出改变的原因有两种:设计和效率。由于一般使用这一方面对final不是很了解(例如我)导致使用目的不是很明确。导致使用错误。而达到反效果。final可以修饰数据,方法,和类

final数据

  1. 被final修饰过的数据,也就是向编译器说这个数据是不可改变了。 或者说在运行时初始化赋值后不可改变。
  2. 在很多情况下我们都需要使用到静态编译器不变的常量。列如我们为了防止代码全局创建多个相同的数据,那我们就在全局定义一些“永不改变的编译时常量”,我们随便打开个源码看看大佬们对final的使用jUC包中的ThreadPoolExcutor类中的应用:
     *    When pool is empty
     * TIDYING -> TERMINATED
     *    When the terminated() hook method has completed
     *
     * Threads waiting in awaitTermination() will return when the
     * state reaches TERMINATED.
     *
     * Detecting the transition from SHUTDOWN to TIDYING is less
     * straightforward than you'd like because the queue may become
     * empty after non-empty and vice versa during SHUTDOWN state, but
     * we can only terminate if, after seeing that it is empty, we see
     * that workerCount is 0 (which sometimes entails a recheck -- see
     * below).
     */
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

    /*
     * Bit field accessors that don't require unpacking ctl.
     * These depend on the bit layout and on workerCount being never negative.
     */

将线程池的代表的状态值使用static和final修饰成为了一个“永不改变的编译时常量”。在这个过程中也不怕被别有用心的人通过某种方式(类似与Java发射)进行修改等。 staitc关键字保证在编译期赋值(static修饰的数据会在类加载过程中分配内存空间且赋值),final保证不会变。 类加载的过程:

其中在准备阶段就会将final修饰的数据给赋予真正的值。

3. 如果final修饰的这个数据是一个对象的时候,他这个对象引用是不会改变的,但是对象自身还是可以改变的,java自身是没有提供保证对象不可变的途径如下代码:

import lombok.Data;
import lombok.ToString;

/**
 * @author yuanxindong
 */
@ToString
@Data
public class FinalUse {
    private String name = "yaunxindong";
    private String gender= "N";
    static final FinalUse finalUse =  new FinalUse();

    public static void main(String[] args) {
        System.out.println("修改前" +finalUse.toString());
        finalUse.setGender("dog");
        System.out.println("修改后"+finalUse.toString());

    }
}

运行结果: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iptzY06K-1590425614844)(http://note.youdao.com/yws/res/13486/9A6FDF832FD34E3085E8CB51C9045F1F)] 如果说我们不希望对象也不被改变,那么我们是否直接使用final进行修饰其中内部的属性,在这个时候本分感觉,这样的话我们就可以使用枚举了。 4. 使用final 修饰方法中的参数,这样修饰的用意是不让方法内修改这个参数,这个用法之前一直没见过,今天看见同事使用了,使用场景是一个业务埋点工具类。所有入参数被修饰。这样有好处吗?有把,如果另一个人维护代码的时候把埋点数据改掉了。。。。。。YY 不敢确定

final方法

  1. 使用final将方法锁定以防止任何继承类修改他的含义。这是出于设计的考虑:想要确保在继承中使方法行为保持不变,并且不会被覆盖
  2. 还有就是效率,在过去如果将一个方法指明为final,就是同意编译器将针对该方法的所有调用都转为内嵌调用。(将参数压入栈,跳至方法代码处执行,然后跳回并清理栈中的参数,处理返回值)并且以方法体中的实际代码的副本来代替方法调用。这将消除方法调用的开销。但是就怕但是,hotspot虚拟机中已经优化掉了上面所说的内存消耗问题。所以也不用使用final来进行优化了(我今天就用final修饰了方法)。
  3. private 修饰的方法,已经隐式指定为final了,但是使用private 修饰的方法仍然可以修饰被final修饰但是没有什么卵用。

final类

  1. 当某个类的整体被定义为final的时候,就表明了我们不想把这个类被被人使用,他也就不可以被继承。换句话说也就是你对该类的设计就是不需要做任何变动,或出于安全考虑,你不希望他有子类。
  2. 被final修饰的类的方法也会隐式成为final,但也可以添加final 但是也不会带来任何意义。

总结

final的作用就是为了更加规范的设计,和更高的效率。正是因为牵扯到了设计和效率,有时他们会产生冲突,我个人觉得在效率方面的是微乎其微,且我们常使用的hotspot已经️对这方面优化了,所以我个人观点就是更偏向设计。

思考

在JVM层面final关键字是起了什么作用呢?