你用过 Java 中的 final 关键字吗?它有哪些作用?这也是出镜率极高的题目哦。
1. final 修饰类的时候代表这个类是不可以被扩展继承的,例如 JDK 里面的 String 类。
2. final 修饰方法的时候代表这个方法不能被子类重写。
3. final 修饰变量的时候代表这个变量一旦被赋值就不能再次被赋值。
想必上面这三点是大家所熟知的,但是下面这 2 点你想到了吗?
缓存
final 变量会被缓存在寄存器中而不需要去主从获取,而非 final 变量的则需要去主存重新加载。
线程可见性
类的 final 域在编译器层面会保证在类的构造器结束之前一定要初始化完成,同时 Java 内存模型会保证对象实例化后它的 final 域对其他线程是可见的,然而非 final 域并没有这种待遇。例如如下代码:
public class FinalFiled {
final int x;
int y;
static FinalFiled f;
public FinalFiled() {
x = 100;
y = 100;
}
static void writer() {
f = new FinalFiled();
}
static void reader() {
if (f != null) {
int i = f.x; // 保证此时一定是 100
int j = f.y; // 有可能此时还是 0
}
}
}
当线程 A 执行了 writer 方法后,有线程 B 会进入 f != null 成立条件的代码块,此时由于变量 x 是 final 修饰的,JMM 会保证 x 此时的值一定是 100,而 y 是非 final 的,则有可能此时 y 的值还是 0,并未被初始化。
安全性
String 类的安全性也得益于恰到好处的使用了大量的 final 域,大家可以去翻翻 String 类的源码。我们来举个例子,假设有线程 A 执行如下代码段:
Global.flag = "/001/002".substring(4);
又有线程 B 执行如下代码段:
String myS = Global.flag;if (myS.equals("/001"))System.out.println(myS);
如果没有 final 域的可见性保证,那么线程 B 在执行的时候有可能看到的字符串的长度可能仍然是 0。当有了 final 域的可见性保证,就可以让并发程序正确的执行。也使得 String 类成为名副其实不可变安全类。