一、final声明属性,表示属性不可变:它只能指向初始时指向的那个对象,而不关心指向对象内容的变化
stop value change中value是指基本数据类型的值或者指向对象的地址不变
//final 变量 对象可改变
final StringBuffer sb=new StringBuffer("abc");
sb.append("d");
System.out.println(sb.toString());
//其引用不可变
sb=new StringBuffer("hh");
/*
The final local variable sb cannot be assigned.
It must be blank and not using a compound assignment
*/
public class FinalAttributeC {
private final Person person = new Person("zhangbingxiao");
public void change(){
person.setName("chengfan");
System.out.println(person.getName());
}
//public void change(Person p){
//this.person = p;
//}
public static void main(String[] args) {
new FinalAttributeC().change();
}
}
//结果 : chengfan
注释掉的代码是会报错的代码,也就是说引用类型person是不可变的,其对象内部的属性是可以改变的。
引用存于栈中,而对象存放于堆中。引用的值是对象在堆中的地址。在本质上,final修饰的是引用,而不是对象。所以引用的哪个地址不可改变,而和对象没多大关系。
a final variable that have no value it is called blank final variable or uninitialized final variable. It can be initialized in the constructor(有static块和实例构造器) only. The blank final variable can be static also which will be initialized in the static block only.
For static final fields, this means that we can initialize them:
- upon declaration as shown in the above example
- in the static initializer block
For instance final fields, this means that we can initialize them:
- upon declaration
- in the instance initializer block
- in the constructor
class A{
static final int data;//static blank final variable
static{ data=50;}
public static void main(String args[]){
System.out.println(A.data);
}
}
被final修饰的变量必须初始化:
①在定义的时候初始化
②final成员变量可以在初始化块中初始化,但不可在静态初始化块中初始化
原因:由(2)知道,单个类的程序加载顺序是:静态变量–>静态代码块–>非静态变量–>非静态代码块–>构造器。
③静态final成员变量可以在静态初始化块中初始化,但不可在初始化块中初始化
原因:static要求在编译期就确定值,然后放入静态区。而非静态部分是在运行期发生的,所以有如上结论
④在类的构造器中初始化,但静态final成员变量不可以在构造函数中初始化
原因:非final变量可以在构造器中初始化,这种final属性在编译期间是无法确定属性值的,只有在运行的时候才确定,增加了灵活性
public class FinalAttributeB {
private final String attribute_b;
public FinalAttributeB(String attribute_b){
this.attribute_b = attribute_b;
}
public void test(){
//attribute_b = "zhangbingxiao";
}
public void test(String attribute_b){
//this.attribute_b = attribute_b;
}
}
二、final方法
If you make any method as final, you cannot override it.
此方法不允许任何子类重写这个方法,但子类仍然可以使用这个方法
public class Person {}
public class Woman extends Person{
/*
* Cannot override the final method from Person
public final void print(){
System.out.println("final method");
}
*/
public static void main(String[] args) {
Woman women=new Woman();
women.print();
}
}
三、final参数
If you declare any parameter as final, you cannot change the value of it.
用来表示这个参数在这个函数内部不允许被修改
public class FinalParam {
public void test(final int a ){
//a = 10; 值不可以被修改
}
public void test(final Person p){
//p = new Person("zhangbingxiao"); 引用本身不可以被修改
p.setName("zhangbingxiao"); //引用所指向的对象可以被修改
}
}
四、final类
If you make any class as final, you cannot extend it.
当一个类被声明为final时,此类不能被继承,所有方法不能被重写(不能继承,重写就无从谈起了)。但这并不表示final类的成员变量也是不可以变的,要想做到final类的成员变量不可改变,必须给成员比那里增加final修饰。在jdk中有很多应用,比如我们熟知的String、Integer等类都被final修改。
五、 immutable class
The class is called immutable if it is impossible to change its state and content after the initialization (creation).
如何写一个不可变类呢?
- 将类声明为final,所以它不能被继承
- 将所有的成员声明为私有的,这样就不允许直接访问这些成员
- 对变量提供getter、不要提供setter方法(read-only)
- 将所有成员变量声明为final,这样只能在constructor(有static块和实例构造器)中初始化【增强成员变量值的灵活性】,且为一次。
- 通过构造器初始化成员,进行深拷贝(deep copy),主要针对mutable引用类型(目的:外部再次对其更改,并不会影响到这个类成员)
- 在getter方法中,不要直接返回对象本身,而是mutable深拷贝对象。(目的:返回后的更改,并不会影响这个成员变量的值)
Q1:一个类不能既被声明为abstract,又被声明为final
原因:final类不可以被继承,而abstract类需要被继承
Q2:final不可修饰构造器?
原因:构造器不可继承,final可继承
import java.util.Date;
import java.util.HashSet;
public final class Dog {
//mutable class
private final Date date;
private final HashSet<A> hs;
//immutable class
private final String str1;
private final Integer it;
public Dog(Date date1, String str1, Integer it,HashSet<A> hs) {
//要这样写,若外部data1修改,不会影响此类
this.date=(Date) date1.clone();
//不能用this.hs=(HashSet<A>)hs.clone(); 因为它是浅复制
this.hs=new HashSet<>(hs);
//能这样写,若外部str修改,并重新创建对象,不会影响此类
this.str1 = str1;
this.it = it;
}
//wrapper class和string均为immutable class ,即使返回的是相同的,若对返回进行修改,会重新创建对象而不是用原对象
public String getStr1() {
return str1;
}
public Integer getIt() {
return it;
}
//Data是mutable class。返回后可能会修改,所以要返回clone副本。
public Date getRemindingDate() {
return (Date) date.clone();
}
}
public class A {
private int i;
public A(int i) {
this.i = i;
}
//getter/setting
}
参考:
https://www.youtube.com/watch?v=R11J9P0gN7s
https://www.geeksforgeeks.org/final-keyword-java/