Java的不可变类

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的值也不会该变。最重要的是这个匿名对象没有变量名字,更无法去修改其值。

总结:如果要设计一个不可变类,那么尤其要注意其引用类型的成员变量,如果引用类型的成员变量的类是可变的,就必须采取措施保护这个成员变量,这样才能真正的创建不可变类。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值