可变类和不可变类用final修饰时的赋值问题
结论:
1.可变类用final修饰,只要不改变引用,改变值还是可以的,可变类传递的时候是引用传递
2.不可变类用final修饰,值和引用都不能改变,不可变类传递的时候是值传递
举例:
以可变类StringBuilder和不可变类String为例
实体类,包含四种成员变量(其实我只用到了两个)
package test_of_class;
public class TestClass {
//注意,不能为final域设置set方法,而且必须在构造函数中为final成员变量进行初始化
//final可变类
private final StringBuilder finalStringBuilder1;
//可变类
private StringBuilder stringBuilder2;
//final不可变类
private final String finalString1;
//不可变类
private String string2;
//构造方法
public TestClass(StringBuilder stringBuilder111,String string111) {
finalStringBuilder1=stringBuilder111;
finalString1=string111;
}
//get,set方法
public StringBuilder getStringBuilder2() {
return stringBuilder2;
}
public void setStringBuilder2(StringBuilder stringBuilder2) {
this.stringBuilder2 = stringBuilder2;
}
public String getString2() {
return string2;
}
public void setString2(String string2) {
this.string2 = string2;
}
public StringBuilder getFinalStringBuilder1() {
return finalStringBuilder1;
}
public String getFinalString1() {
return finalString1;
}
//toString方法,如果不重写,调用的时候打印的是对象的地址,比如[email protected]
@Override
public String toString() {
return "TestClass [finalStringBuilder1=" + finalStringBuilder1 + ", stringBuilder2=" + stringBuilder2
+ ", finalString1=" + finalString1 + ", string2=" + string2 + "]";
}
}
下面是测试类,需要注意的是两次打印实体类的toString时,两个final修饰的成员变量的值是否有变化
package test_of_class;
public class TestClassMain {
public static void main(String[] args) {
TestClass testClass=new TestClass(new StringBuilder("stringBuilder111"), "string111");
//打印结果: TestClass [finalStringBuilder1=stringBuilder111, stringBuilder2=null, finalString1=string111, string2=null]
System.out.println(testClass.toString());
//1.StringBuilder是可变类(虽然在StringBuilder中有final修饰)
//在TestClass中有final修饰,但是由于是可变类,因此只要不改变引用,改变值还是可以的,可变类传递的时候是引用传递
StringBuilder finalStringBuilder1=testClass.getFinalStringBuilder1().append("fff");
//打印结果: true,证明是引用传递
System.out.println(finalStringBuilder1==testClass.getFinalStringBuilder1());
//2.String是不可变类,有final修饰
//在TestClass中有final修饰,而且还是不可变类,因此值和引用都不能改变,不可变类传递的时候是值传递
String finalString1=testClass.getFinalString1()+"fff";
//打印结果: TestClass [finalStringBuilder1=stringBuilder111fff, stringBuilder2=null, finalString1=string111, string2=null]
System.out.println(testClass);
//打印结果: false,证明是值传递
System.out.println(finalString1==testClass.getFinalString1());
}
}
分析
先调用了构造器,为两个final变量初始化(必须在创建对象的时候为final变量赋值),赋值如下
TestClass testClass=new TestClass(new StringBuilder("stringBuilder111"), "string111");
可以看到第一次打印结果是符合上面的赋值的
由于final变量不能生成set方法,因此试图修改两者的值的时候只能通过get方法,看看是否能够成功,修改方式如下
//尝试修改final修饰的StringBuilder类型变量
StringBuilder finalStringBuilder1=testClass.getFinalStringBuilder1().append("fff");
//尝试修改final修饰的String类型的变量
String finalString1=testClass.getFinalString1()+"fff";
由结果分析,只有StringBuilder类型的变量的值变化了
原因是
1.append()方法是更改器方法,是能够修改原对象的,同时,getFinalStringBuilder1()方法将这个StringBuilder对象的地址赋值给了finalStringBuilder1,这是引用传递,两者一起拼接了"fff"
final修饰的可变类,可改值,不能改引用
(想改引用,没有set方法改不了)
注意:<<java核心技术>>一书中曾提到,最好不要编写返回引用可变对象的访问器方法(get方法就是访问器方法),因为这个可变对象的值是能够被修改的,这样就破坏了封装性
2.String是不可变类,该类中没有更改器方法,就是说没有修改原来对象数据的途径,因此在尝试修改string值的时候,是值传递,传递过来的是值,而不是引用,main中的finalString1对象指向了新字符串的同时,testClass中的finalString1是没有被修改的
final修饰的不可变类,值和引用都不能改
补充:基本数据类型的包装类和String都是不可变类
参考:<<java 核心技术 卷Ⅰ>>