小议 Java final 关键字

版权声明:作者:N3verL4nd 出处: https://blog.csdn.net/lgh1992314/article/details/80015408

昨日面试美团,被问到 final 关键字,总觉得可以再进行总结一下了。
因为你所掌握的现有知识又会再次颠覆你原来的理解。

final 用于属性

语义:被初始化后不能被更改

class Main {
    private final int x;
}

以上代码会报错:x 没有被初始化。
而对没有 final 关键字修饰的属性是会执行默认初始化的。
这里写图片描述
解决办法:

class Test {
    private final int x;
    // 1
    //private final int x = 0;

    // 2
    /*Test() {
        x = 0;
    }*/

    // 3
    /*Test(int x) {
        this.x = x;
    }*/

    // 4
    /*{
        x = 100;
    }*/
}

在我眼中,没有被 static final 关键字修饰的final属性必须在实例初始化阶段进行初始化(构造函数或者构造代码块或者定义时),而且初始化后不能再进行赋值。在 JVM 层面该初始化对应于<init> 方法的执行。–>这叫实例初始化。

但是 static final 修饰的属性在准备阶段(加载–>验证–>准备–>解析–>初始化)完成初始化。

public class Main {
   private final int x = 100;
}

对应的字节码文件:
这里写图片描述

但是 Java 中有反射啊!!

package test;

import java.lang.reflect.Field;

class Demo {
    final int x = 100;
}

public class Main {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Demo demo = new Demo();
        System.out.println(demo.x); // 100
        Field x = demo.getClass().getDeclaredField("x");
        x.setAccessible(true);
        x.setInt(demo, 200);
        System.out.println(demo.x); // 100
    }
}

因为 xfinal 修饰,所以我们在调用实例.x 时,都会被优化为 100,因为它是不能在初始化后修改的。提高效率。
以上System.out.println(demo.x); 直接被优化为 System.out.println(100);

以上代码并不能说明反射不能更改 final 属性。因为 final 更多的强调编译阶段(编译阶段执行检查以及优化)。

验证:
这里写图片描述

对于访问 final 属性的方法:
这里写图片描述

final 用于方法

语义:final 方法不能被重写

这里写图片描述

那么为何这里的调用是 invokevirtual 呢?既然不能被重写,那么就不能构成多态了!那么为何不是 invokespecial 呢?
见 R 大分析:
https://www.zhihu.com/question/45131640

final 用于类

语义:final 类不能被继承
基本类型所对应的包装类型 Byte,Short,Character,Integer,Long,Float,Double 都是 final 类。

猜你喜欢

转载自blog.csdn.net/lgh1992314/article/details/80015408