不可变类是指创建类的对象实例后,该实例的属性不能发生改变。常见的String就是不可变类。不可变类型的属性值不会发生改变,这在多线程编程的时候非常有用,不用担心对象的属性值被修改。
下面我们来看看如何实现一个不可变类型:
1.要保证属性值不发生改变,属性必须用private和final修饰。
2.要在类的构造函数中初始化final属性,并且只提供getter方法,不提供setter方法。
3.重写equals和hashCode,使用重写的equals方法根据属性值判断两个对象是否相等,hashCode方法保证不同的对象,hashCode不同。
最简单的不可变类实现方法:
class Person { private final String firstName; private final String lastName; public Person(String firstName,String lastName) { this.firstName=firstName; this.lastName=lastName; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } @Override public boolean equals(Object obj) { if(obj instanceof Person) { Person p=(Person)obj; return this.firstName.equals(p.firstName)&&this.lastName.equals(p.lastName); }else { throw new ClassCastException(); } } @Override public int hashCode() { return (this.firstName+this.lastName).hashCode(); } }
Person类就是一个不可变类,属性都是private和final类型,并且只提供了getter方法,根本无法修改属性的值。
但是如果一个类中包含了一个可变类型那就不能这么简单的实现了。
class Name { private String firstName; private String lastName; public Name() {} public Name(String firstName, String lastName) { this.setFirstName(firstName); this.setLastName(lastName); } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } } class Person { private final Name name; public Person(Name name) { this.name=name; } public Name getName() { return name; } @Override public String toString() { return name.getFirstName()+"$"+name.getLastName(); } }
Person类中含有一个可变类型Name,虽然Name被final和private修饰,并且只有getter,但是仍然可以修改Name的值:
Person person=new Person(new Name("Hello","world")); System.out.println(person); person.getName().setFirstName("hel"); System.out.println(person);
输出结果:
Hello$world
hel$world
很明显不能达到不可变类的目的,要想变成不可变类,需要在getter中返回一个新的对象,在构造器中也要用新的对象初始化:
public Person(Name name) { this.name=new Name(name.getFirstName(),name.getLastName()); } public Name getName() { return new Name(name.getFirstName(),name.getLastName()); }
这样就完全不会改变对象实例的值了。String类就是使用了类似的实现方法,不过使用的常量缓存池,大大提高了性能。每次初始化String对象是都会先检查常量缓存池,如果已经存在则不会再内存中创建新的常量字符串了。
原文:https://www.cnblogs.com/yq-blog/p/8944876.html