一文搞懂 final 关键字

123 阅读3分钟

1. final 关键字

final:最终的,被 final 关键字修饰,就不可以改变了。

final 可以修饰:

  • 变量:表明该变量是常量,只能被赋值一次。
  • 方法:表明该方法是最终方法,不能被重写。不想被别人改写的方法。
  • :表明该类是最终类,不能被继承。

注:final 不可以修饰构造方法

2. 使用 final 修饰变量

2.1 编译时常量

Java 编译期常量(compile-time constants)是指在编译时期就能被完全确定并被编译器替换其值的常量,减轻运行时负担。编译期常量:

  1. 声明为 final基本数据类型String 类型变量,并且在声明时进行初始化赋值
  2. 初始值必须是一个在编译时就能确定的常量表达式,例如字面值常量或其他编译期常量的组合。
private final int valueOne = 1;
private static final int VALUE_TWO = 2;

valueOneVALUE_TWO 均为编译期常量。使用 static 修饰的编译期常量全部使用大写字母命名,使用下划线连接。

当 final 修饰的变量是引用类型时,变量存储的地址值不能发生改变,对象内部可以改变。

public class Test {
    public static void main(String[] args) {
        final Student student = new Student("zhang",  18);
        student.setName("Li");
        student.setAge(20);
        System.out.println(student.getName() + ":" +student.getAge());// Li:20
        final int[] arr = {0, 1, 2, 3};
        arr[0] = 11;
        System.out.println(arr[0]);// 11
    }
}

为什么字符串是不可变的?

// String.java
@Stable
private final byte[] value;

String 类中使用 final 修饰存储字符串的数组value,value指向的地址值不会改变。那存储的内容为什么不会改变呢?value 是 private 修饰的,并且 value 并没有提供 getter 和 setter 方法,所以字符串内容不会发生改变。

2.2 在运行时被初始化的常量

并不是使用 final 修饰的数据在编译时就可以知道它的值。

private static final int VALUE_THREE = new Random().nextInt(10);

VALUE_THREE 在运行时才会知道它的值。

3. 使用 final 修饰方法

3.1 将方法锁定

使用 final 修饰方法为了将方法锁定,以防止任何类修改方法含义,出于设计的考虑,想要确保在继承中方法行为保持不变。不可被重写,常用于模板方法模式。

3.2 效率(现已过时)

在 Java 的早期实现中,如果将一个方法指明为 final,就是同意编译器将针对该方法的所有调用都转为内嵌调用。现在 Java 的实现中,虚拟机可以探测到这些情况,并优化去掉一些效率反而降低的额外的内嵌调用,因此不再需要使用 final 方法来进行优化了。

3.3.final 与 private

类中所有的 private 方法都隐式地指定为是 final 的。由于无法取用 private 方法,所以也就无法覆盖它。可以对 private 方法添加 final 修饰词,但这并不能给该方法增加任何额外的意义。

4. 使用 final 修饰类

使用 final 修饰类时,该类不能进行任何改变,也不能被继承。

由于 final 类禁止继承,所以 final 类中所有的方法都隐式指定为是 final 的。在 final 类中可以给方法添加 final 修饰词,不会增添任何意义。