JavaSE---final
用途
修饰 类、变量、方法;
作用:
表示 修饰的类、变量、方法 不可改变;
final修饰变量
对final修饰的变量进行修改,编译报错;
可以修改final修饰的引用对象的值;
static class Person{ private String name; public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } } static class FinalField{ final byte a = 1; final short b = 1; final int c = 1; final long d = 1L; final float e = 1f; final double f = 1d; final char g = 'a'; final boolean k = false; final int[] arr = {1,2,3}; final String s = "a"; final Person person = new Person("jack"); void test(final int cc){ // ===对final修饰的变量进行修改,编译报错 // 成员变量 a = 1; b = 1; c = 1; d = 1; e = 1; f = 1; g = 1; k = true; arr = new int[]{4,5,6}; s = "b"; person = new Person("rose"); // 形参 cc = 1; // 局部变量 final int dd = 1; dd = 2; // ===可以修改final修饰的引用对象的值 person.setName("rose"); } } public static void main(String[] args) { FinalField finalField = new FinalField(); finalField.test(); System.out.println(finalField.person); // Person{name='rose'} }
static final
static final修饰的变量是 类变量,类加载阶段初始化;
必须显式初始化(或先声明变量,后在static块中初始化)
static class FinalField{ static final int m = 11; static final int n; static final Person p = new Person("nono"); static { n = 12; } }
blank final
字段被声明为final ,但又没有给出定值的字段,但是必须在该字段被使用之前被赋值;
可以在 构造器 中赋值;
static class FinalField{ final Person pp; FinalField(){ pp = new Person("mm"); } }
2.1、 修饰变量时,一旦 该变量 获得初始值 后 不可被改变(重新赋值);
2.2、final 既可以 修饰 成员变量(类变量、实例变量),也可以修饰 局部变量 && 形参;
2.3、final修饰成员变量:
成员变量 随 类初始化 或 对象初始化 而初始化;
当类初始化时,系统为该类的类属性分配内存,分配默认值;
执行静态代码块时,对类属性 赋初始值;
当创建对象时,系统为该对象的实例属性分配内存,分配默认值;
执行普通初始化块、构造器时,为 实例属性赋初始值;
成员变量的初始值什么时机可以指定?
定义该变量时指定;
初始化块;
构造器;
如果不显式指定初始值,将由系统分配默认的初始值(0、\u000、false、null);
final修饰的成员变量,一旦被赋初始值,将不能被重新赋值,因此不可以在 方法中、初始化块、构造器 对其进行重新赋值;
public class FinalTest { final Person person; { person = new Person("rose"); } public static class Person{ String name; public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } } public static void main(String[] args) { FinalTest finalTest =new FinalTest(); System.out.println(finalTest.person);// Person{name='rose'} } }
2.4、final修饰局部变量
系统不会对局部变量进行初始化;
局部变量 必须由 程序员 显式初始化;
final修饰局部变量,可以在定义时指定,也可以在后续指定,一旦指定,则不可以 重新赋值;
2.5、final修饰基本类型
final修饰基本类型变量时,不能对基本类型变量重新赋值;
2.6、final修饰引用类型
final修饰引用类型,保存的仅仅是一个引用,final只保证这个引用地址不变(即引用同一个对象),但这个对象可以改变;
public class FinalTest { public static class Person{ String name; public Person(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } } public static void main(String[] args) { final Person person = new Person("rose"); System.out.println(person);// Person{name='rose'} person.setName("jack"); System.out.println(person);// Person{name='jack'} } }
2.7、final修饰的变量
如果是基本类型,在编译期就可以确定,可以按常量命名;
如果是引用类型,编译期确定不了,需要运行时确定,无需按常量命名;
final修饰类
不可以 被继承;
如何对final修饰的Class进行扩展?
组合模式;
static class MyString{ private final String innerString; public MyString(String s){ this.innerString = s; } public int length(){ return innerString.length(); } }
final修饰方法
不可以被重写;
如果处于某些原因,不希望子类重写父类方法,可以使用final;
若子类 重写 父类final方法,则编译出错;
比如 父类中的 private final方法,在子类中有同名、同参 也是允许的,被当做子类的方法;
public class FinalTest { public static class Person{ private final void test(String name){ } } public static class Son extends Person{ public void test(String name){ } } }
可以重载;
class Man{ final void test(){ } final void test(int c){ } }
final重排序规则
按照final修饰的数据类型分类:
-
基本数据类型:
final域写
:禁止final域写与构造方法重排序,即禁止final域写重排序到构造方法之外,从而保证该对象对所有线程可见时,该对象的final域全部已经初始化过。final域读
:禁止初次读对象的引用与读该对象包含的final域的重排序。
-
引用数据类型:
额外增加约束
:禁止在构造函数对一个final修饰的对象的成员域的写入与随后将这个被构造的对象的引用赋值给引用变量 重排序
final的实现原理
写final域 会要求编译器在final域写之后,构造函数返回前插入一个StoreStore屏障;
读final域 会要求编译器在读final域的操作前,插入一个LoadLoad屏障;