Java经典语法系列
- Java面试宝典:final语义深度分析
- Volatile深度剖析-可见性
- Volatile深度剖析-原子性
- Volatile深度剖析-指令重排序
- Java经典语法糖:你真的理解泛型吗?
背景
在实际项目开发中,我们常常用final修饰的变量存储常量值,使得程序看起来优雅美观。
public static final String HIST_INDEX = "histpenddb";
更重要的是让我们放心安全的使用这个常量,不必担心常量值会变化。
基本语法
1、被final修饰的类不能被子类继承
final class FinalClass{}
final类不能被继承,所以没有子类。
在设计Java类时,如果这个类不需要有子类,其内部结构不允许被改变并且确信这个类以后不会被扩展修改,那么可以把这个类定义成final类。
2、被final修饰的方法不能被子类重写,但可以被继承
class FinalClass{ public final void finalTest(){}}
3、被final修饰的字段值不可修改
class FinalClass{ public static final boolean ENABLED = true; public final void finalTest(){} }
4、类中所有的私有方法(private)都隐式指定为final,因此私有方法默认是final类型的
5、final不能修饰构造方法
由第四条可知,构造方法变私有,就不能实例化对象。
内存语义
我们先了解下Java内存模型的重排序规则,这有利于我们理解final的内存语义。
重排序规则
Java为提高程序的执行性能,会对程序编码进行语义上的重排序。
重排序流程
重排序规则分为三大类
- 编译级重排序
- 指令级重排序
- 内存级重排序
其中指令级重排序、内存级重排序属于处理器重排序。
编译级重排序
不改变单线程程序语义前提下,重新安排指令的执行顺序
指令级重排序
指令并行技术可以将多条指令重叠执行,如果不存在数据依赖性,处理器会改变语句对应的机器指令执行顺序
内存屏障
为了防止一些重排序规则会改变程序计算结果,我们需要使用一些手段来禁止不必要重排序规则,称之为内存屏障。
就像我之前在《Volatile深度剖析-指令重排序》文章中举的栗子
白话来讲就是往你的一些代码里面动态插入一些屏障指令,目的是防止在多线程环境下,变量赋值错乱问题。这个就好比你家种的白菜,你是不是会担心白菜叶子会被鸡吃掉,那你会怎么做呢?想到什么了?对,用围栏把菜园子围起来,这样可以防止鸡偷吃白菜叶子。
内存屏障分类
final内存语义
Java禁止final类型的写操作重排序到构造方法之外。
也就是说,类的成员变量必须在构造方法体初始化,当然final类型的成员变量也可以在定义时初始化。
另外,从编译器优化角度来说,会在final类型写入后,在构造方法返回之前插入内存屏障(StoreStore),以防止编译器重排序。
由重排序规则和final内存语义可以看出,java设计final关键字的初衷:
在多线程环境下,为了实现类似于共享数据的需求,需把某些字段值变为常量,以便程序能够安全稳定的调用,不会因为字段值变化而导致程序执行错乱问题。