1、什么是不变性
- 对象在被创建后,状态不能被修改,那么它就是不可变的
- 具有不可性的对象,线程一定是安全的,因为无法修改
//这个类中有一个变量是可变的,这个类就不具备不变性 public class Person { final int age = 18; final String name = "Alice"; int score = 0; }
写一个测试类,测试修改final修饰的变量,编译器会直接报错
2、final的作用
- 防止类被继承、方法防止被重写、变量防止被修改
- 天生是线程安全的,而不需要额外的同步开销
2.1、final修改变量
被final修饰的变量,意味着值不能被修改。如果变量时对象,那么对象的引用不能改变,但是对象本身的内容依然可以变换
public class TestFinal { public static void main(String[] args) { //final修饰类,引用不能变,但是类的属性可变 final Person person = new Person(); person = new Person(); person.score = 30; } }
3、赋值的时机
3.1、普通final属性
- 第一种在声明变量的等号右边直接赋值
- 第二种就是在构造函数中赋值
- 第三种就是在类的初始化代码块中赋值
//方式1 public class FinalVariableDemo { private final int a ; } //方式2 public class FinalVariableDemo { private final int a ; public FinalVariableDemo(int a) { this.a = a; } } //方式3 public class FinalVariableDemo { private final int a ; { a = 7; } }
3.2、静态final属性
- 在右边直接赋值
- 静态代码块的赋值
//方式1 public class FinalVariableDemo { private static final int a = 10; } //方式2 public class FinalVariableDemo { private static final int a ; static { a = 7; } }
3.3、方法中final变量
- 需要在使用之前进行赋值
public class FinalVariableDemo { public void test(){ final int b; //需要在使用之前赋值,不赋值会报错 a = b; } }
4.final修饰方法
- 构造方法不允许final修饰
- 不可被重写
public class FinalMethodDemo { public void drink(){ } public final void eat(){ } } class SubClass extends FinalMethodDemo{ @Override public void drink() { super.drink(); } //会报错,无法重写 public void eat(){ } }
5、final修饰类
- 不可被继承
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { }
6、final注意点
- final修饰对象的时候,只是对象的引用不可变,而对象本身的属性是可以变换的
- fnal使用原则:良好的变成习惯
7、不变性和final的关系
- 基本数据类型,被final修饰后就是不可变的
- 对象类型,创建后属性永远不可变,才是具备不可变性
满足一下条件时,对象才是不可变的:
- 对象创建后,其 就不能修改
- 所有属性都是final修饰
- 对象创建过程中没有发生溢出
7.1、栈封闭
public class StackConfinement implements Runnable { int index = 0; public void inThread(){ int neverGoOut = 0; for (int i = 0; i < 1000; i++) { neverGoOut++; } System.out.println("栈内保护的数字是线程安全的:" + neverGoOut); } @Override public void run() { for (int i = 0; i < 10000; i++) { index++; } inThread(); } public static void main(String[] args) throws InterruptedException { StackConfinement r = new StackConfinement(); Thread thread1 = new Thread(r); Thread thread2 = new Thread(r); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(r.index); } }
8、常见面试题
public class finalStringDemo { public static void main(String[] args) { String a = "wukong2"; //被final修饰的String会被当成常量进行运算 final String b = "wukong"; String d = "wukong"; String c = b + 2; String e = d + 2; System.out.println(a == c); System.out.println(a == e); } }
public class finalStringDemo2 { public static void main(String[] args) { String a = "wukong2"; final String b = getDashixiong(); String c = b + 2; System.out.println(a == c); } //通过编译器获取,不回提前生成,所以是false public static String getDashixiong(){ return "wukong"; } }