什么是不变性(Immutable)
- 如果对象在被创建后,状态就不能被修改,那么它就是不可变的
- 具有不变性的对象一定是线程安全的,我们不需要对其采取任何额外的安全措施,也能保证线程安全
final
- 在java中,final可以用来修饰类,方法和变量(成员变量或局部变量)。
final的作用
- 早期:早期的Java实现版本中,会将final方法转为内嵌调用
- 现在:类防止被继承、方法防止被重写、变量防止被修改,天生是线程安全的,而不需要额外的同步开销
修饰方法
- final不可以修饰构造方法
- 静态方法也不可以被重写 使用final方法的原因主要有两个:
- 1、把方法锁定,以防止继承类对其进行更改。
- 2、效率,在早期的java版本中,会将final方法转为内嵌调用。但若方法过于庞大,可能在性能上不会有多大提升。因此在最近版本中,不需要final方法进行这些优化了。
- final方法意味着“最后的、最终的”含义,即此方法不能被重写。
注意:若父类中final方法的访问权限为private,将导致子类中不能直接继承该方法,因此,此时可以在子类中定义相同方法名的函数,此时不会与重写final的矛盾,而是在子类中重新地定义了新方法。
修饰变量
final成员变量表示常量,只能被赋值一次,赋值后其值不再改变。类似于C++中的const。
当final修饰一个基本数据类型时,表示该基本数据类型的值一旦在初始化后便不能发生变化;如果final修饰一个引用类型时,则在对其初始化之后便不能再让其指向其他对象了,但该引用所指向的对象的内容是可以发生变化的。本质上是一回事,因为引用的值是一个地址,final要求值,即地址的值不发生变化。
final修饰一个成员变量(非静态),必须要显示初始化
- 1、是在变量声明的时候初始化;
- 2、是在声明变量的时候不赋初值,但是要在这个变量所在的类的所有的构造函数中对这个变量赋初值。
- 3、是在类的初始化代码块中赋值(不常用)
final修饰静态成员变量(静态),也必须要显示初始化
- 1、是在变量声明的时候初始化
- 2、在static静态代码块中赋值
final修饰的局部变量
- 不规定赋值时机,只是要求在使用前赋值
当函数的参数类型声明为final时,说明该参数是只读型的。即你可以读取使用该参数,但是无法改变该参数的值。
修饰类
当用final修饰类的时,表明该类不能被其他类所继承。当我们需要让一个类永远不被继承,此时就可以用final修饰,但要注意:final类中所有的成员方法都会隐式的定义为final方法。
在java中,String被设计成final类,那为什么平时使用时,String的值可以被改变呢?
字符串常量池是java堆内存中一个特殊的存储区域,当我们建立一个String对象时,假设常量池不存在该字符串,则创建一个,若存在则直接引用已经存在的字符串。当我们对String对象值改变的时候,例如 String a="A"; a="B" 。a是String对象的一个引用(我们这里所说的String对象其实是指字符串常量),当a=“B”执行时,并不是原本String对象("A")发生改变,而是创建一个新的对象("B"),令a引用它。
final注意点:
- final修饰对象的时候,只是对象应用不可变,而对象本身的属性是可以变化的
- final使用原则:良好的编程习惯
不变性和final的关系
- 不变性并不意味着,简单的用final修饰就是不可变的
- 对于基本数据类型,确实被final修饰后就具有不可变性
- 但是对于对象类型,需要该对象保证自身被创建后,状态永远不变才可以
满足以下条件时,对象才是不可变的
- 对象创建后,其状态就不能修改
- 所有属性都是final修饰的
- 对象创建过程中没有发生溢出
把变量写在线程内部——栈封闭
- 在方法里新建的局部变量,实际上是存储在每个线程的私有栈空间的,而每个栈的空间是不能被其他线程所访问的,所以不会有线程安全问题。这就是著名的“栈封闭”技术,是“线程封闭”技术的一种情况
参考: https://www.cnblogs.com/ktao/p/8586966.html