行文思路:
1 基本用法
2 final 关键字原理
3 与static关键字的区别
-----------------------------------------------------
1 基本用法:
a,final关键字修饰类。表明这个类不能被继承,一般来讲,除非这个类专门不让别人继承或者出于安全考虑,一般不要将类设置为final,此外,final修饰的类的成员方法会被隐式的制定为final 方法。
b, final关键字修饰方法。可以防止任何继承类修改这个方法。
c, final关键字修饰变量。基本数据类型或者String类型在编译的时候就能知道其确切值,一旦初始化之后就不能被改变。如果是引用类型,初始化之后是不能被改变指向另一个对像的,但是该内存地
址中保存的对象信息, 是可以进行修改的。
2 final 原理
对于final域,编译器和处理器要遵守两个重排序规则:
1.在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
(先写入final变量,后调用该对象引用)
原因:编译器会在final域的写之后,插入一个StoreStore屏障
2.初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。
(先读对象的引用,后读final变量)
编译器会在读final域操作的前面插入一个LoadLoad屏障
第一种情况:写普通域的操作被编译器重排序到了构造函数之外
而写 final 域的操作,被写 final 域的重排序规则“限定”在了构造函数之内,读线程 B 正确的读取了 final 变量初始化之后的值。
写 final 域的重排序规则可以确保:在对象引用为任意线程可见之前,对象的 final 域已经被正确初始化过了,而普通域不具有这个保障。
第二种情况:读对象的普通域的操作被处理器重排序到读对象引用之前
而读 final 域的重排序规则会把读对象 final 域的操作“限定”在读对象引用之后,此时该 final 域已经被 A 线程初始化过了,这是一个正确的读取操作。
读 final 域的重排序规则可以确保:在读一个对象的 final 域之前,一定会先读包含这个 final 域的对象的引用。
如果 final 域是引用类型
对于引用类型,写 final 域的重排序规则对编译器和处理器增加了如下约束:
在构造函数内对一个 final 引用的对象的成员域的写入,与随后在构造函数外把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
3 与static关键字的区别
static 只是保存了一个副本,在初始化的时候被创建了,final 的作用是保障不可变,是两个东西。