概述
final 在英文中是最终的, 不可更改的. 在 Java 中 final 修饰变量, 函数和类, 就像这个单词的意思, 一旦使用赋值之后不可更改.
final 修饰的变量不可以被改变
finalTest 类
- public class finalTest
- {
- private String test = "test";
- //final 修饰的成员变量 第一种赋值方式
- private final int a = 6;
- private final int b;
- private final int c;
- //final 修饰的静态成员变量 第一种赋值方式
- private final static int e = 6;
- private final static int f;
- public finalTest()
- {
- //final 修饰的成员变量 第二种赋值方式
- b = 5;
- }
- {
- //final 修饰的成员变量 第三种赋值方式
- c = 5;
- }
- static
- {
- //final 修饰的静态成员变量 第二种赋值方式
- f = 6;
- }
- public void test(final int g)
- {
- //final 修饰的局部变量第一种方式
- final int d = 10;
- //final 修饰的局部变量第二种赋值方式
- final int h;
- h = 11;
- // 调用此函数的已经进行赋值, 再次赋值会报错
- //g = 66;
- }
- @Override
- public String toString()
- {
- return test;
- }
- public static void main(String[] args)
- {
- final finalTest finalTest = new finalTest();
- //final 修饰, 无法被改变
- //finalTest = null;
- System.out.println(finalTest);
- finalTest.test = "test2";
- System.out.println(finalTest);
- }
- }
输出
test test2
final 修饰基本数据类型, 不能对基本数据类型重新赋值, 基本数据类型变量不能被改变. 但 final 修饰引用类型变量, 不变的仅仅是他的一个引用, 只要引用地址不变, 他里面的成员变量是可变的.
final 修饰的函数不可以被重写
finalTest 类
- public class finalTest
- {
- public final void test()
- {
- System.out.println("父类");
- }
- }
- class finalTestChild extends finalTest
- {
- // @Override
- // public void test()
- // {
- //
- // }
- //final 修饰的函数可以被重载
- public void test(int a)
- {
- System.out.println("重载");
- }
- }
父类被 final 修饰的函数是无法被子类重写的, 但 final 修饰的函数可以被重载.
final 修饰的类不可以被继承
最典型是的例子是 Java 的 String 类, 打开 String 类发现 String 类是被 final 所修饰.
finalTest 类
继承一个被 final 修饰的父类, 就会报错. 很明显 Java 设计人员不希望我们对 String 类进行修改, 个人理解是因为 String 类过于强大, Java 的设计人员出于安全考虑, 不希望它有子类. 因为这样可能危及到系统安全. 所以 final 类中所有的类都隐式指定为是 final 的, 无法覆盖他们, 我们只能使用他规定的函数. 所以当我们希望自己的类不被人继承时, 就可以指定为 final.
final 的作用
效率, JVM 和 Java 都会缓存 final 变量并对函数, 和类进行优化.
设计和安全, 上 "锁", 不希望自己的类和函数被人随意改变.
final 的安全发布
创建一个对象, 大致可以分为三个步骤
分配内存空间
将引用指向分配的内存空间
调用构造函数来初始化对象
这三个步骤不是原子的, 执行到第二部, 没进行初始化, 此时如果这个对象能被当前范围之外的代码所使用, 因为这时对象已经不是 null 了, 被其他代码访问, 会得到一个错误的结果. 这就是不安全的发布. 所谓安全发布, 简单理解就是对象的创建能够保障在被别人使用前, 完成数据的构造设置, 或者说一个对象使用时, 已经进行初始化. 但是 Java 对此并没有进行保障, 需要自己进行保障设置, 如锁机制等.
对于 final, 当创建一个对象时, 使用 final 关键字能够使得另一个线程不会访问到处于 "部分创建" 的对象.
当构造函数退出时, final 字段的值保证对访问构造对象的其他线程是可见的.
一旦对象引用对其他线程可见, 则其 final 成员也必须正确赋值.
所以借助 final, 就如同是你对对象的创建访问加锁了一样, 天然保障了对象的安全发布
总结
许多并发错误都是没理解共享对象的既定规则, 当发布一个对象时, 必须明确说明对象的访问方式.
来源: https://www.cnblogs.com/dslx/p/10592120.html