1、定义:如果一个类创建实例后,该实例的实例变量不可以改变,那么这个类就是不可变类。
2、创建方式:
(1)使用private和final修饰符来修饰该类的成员变量
(2)提供带参数的构造器,通过传入的参数来初始化该类中的成员变量
(3)仅为该类提供getter方法,而不提供setter方法
(4)如果有必要,重写该类的hashCode()和equals()方法,正常情况下,用equals()判断两个对象相等,那么这两个对象的hashCode()也相等
3、例子
class Address{
//将实例变量用private和final修饰
private final String detail;
private final String postCode;
//提供带参构造器
public Address(String detail,String postCode) {
this.detail=detail;
this.postCode=postCode;
}
//仅仅提供getter方法
public String getDetail() {
return this.detail;
}
public String getPostCode() {
return this.postCode;
}
//重写equals()方法
public boolean equals(Object obj) {
if(this==obj) {
return true;
}
if(obj!=null && obj.getClass()==Address.class) {
Address ad=(Address) obj;
//只有detail和postCode都相等,这两个对象才相等
if((this.getDetail()==ad.getDetail()) && (this.getPostCode()==ad.getPostCode())){
return true;
}
}
return false;
}
//重写hashCode()方法
public int hashCode() {
return detail.hashCode()+postCode.hashCode(); //只有detail和postCode都相等,这两个对象的hashCode才相等
}
}
public class Test{
public static void main(String[] args) {
Address ad1=new Address("China","11011");
Address ad2=new Address("America","11011");
Address ad3=new Address("China","11011");
System.out.println(ad1.equals(ad2)); //false
System.out.println(ad1.equals(ad3)); //true
System.out.println(ad1.hashCode()); //111838567
System.out.println(ad3.hashCode()); //111838567
}
}
Address类中,没有定义setter方法,因此创建了ad1、ad2、ad3实例后,无法更改这些实例的detail和postCode
4、注意:一个不可变类中如果存在引用类型的成员变量,而这个引用类是可变类,那么这个类也变成了可变类
//可变类
class Person{
private int age;
private String name;
public Person(int age,String name) {
this.setAge(age);
this.setName(name);
}
//name变量的setter和getter方法
public void setName(String name) {
if(name.length()<2||name.length()>6) {
System.out.println("输入的名字不符合要求");
}
else {
this.name=name;
}
}
public String getName() {
return this.name;
}
//age变量的setter和getter方法
public void setAge(int age) {
if(age<0||age>100) {
System.out.println("输入的年龄不符合要求");
}
else {
this.age=age;
}
}
public int getAge() {
return this.age;
}
}
//不可变类
class Student{
private final Person p; //定义一个引用类型的成员变量
//带参构造器
public Student(Person p) {
this.p=p;
}
//只提供getter方法
public Person getPerson() {
return this.p;
}
}
public class Test{
public static void main(String[] args) {
Person p=new Person(23,"张三"); //创建一个Person实例
Student s=new Student(p); //创建一个Student实例,这个实例指向p对象,并且这个实例的指向不可改变
System.out.println(s.getPerson().getAge()); //输出23
p.setAge(78);
System.out.println(s.getPerson().getAge()); //输出78
s=null; //不报错,因为Student类已经不再是不可变类
}
}
可以看到不可变类Student类创建的s实例,其中的值改变了,因此这个类已经不再是不可变类,而是可变类
底层运行机制如下图所示:
由于p是可变的,所以导致了s也变成可变的,从而Student类不再是不可变类。
如何修改才能让Student类一定是不可变类??? 必须保护好Student类中的成员变量:p,让程序无法访问到这个p才行
class Student{
private final Person p; //定义一个引用类型的成员变量
//带参构造器
public Student(Person p) {
//创建一个新的Person对象,并让这个对象与传入的Person对象的age、name相同
//此时这个p指向的是新的Person对象,不在是刚才创建的Person对象了
this.p=new Person(p.getAge(),p.getName());
}
//只提供getter方法
public Person getPerson() {
//返回一个匿名对象,这个对象的age和name与p对象相同
return new Person(p.getAge(),p.getName());
}
}
public class Test{
public static void main(String[] args) {
Person p=new Person(23,"张三");
Student s=new Student(p);
System.out.println(s.getPerson().getAge()); //输出23
p.setAge(78);
System.out.println(s.getPerson().getAge()); //输出23
}
}
采用匿名的Person对象来创建Student对象,此时这个s不再直接指向p,而是指向一个与p对象的age、name都相同的匿名对象,那么操作p对象时,这个匿名对象并不会被访问到,因此s不会改变。
实际的运行机制如下图:
因此,修改p的值并不会改变这个匿名对象的值,故s的值也不会该变。最重要的是这个匿名对象没有变量名字,更无法去修改其值。