=======================《疯狂Java讲义精粹》读书笔记8 ------ 不可变类========================
当我们想创建一个实例后,要求该实例的成员不再发生改变的时候,就需要使用不可变类(immutable).
创建自定义的不可变类,应遵守如下规则:
· 使用private 和 final修饰该类的成员变量
· 提供带参数构造器,用于传入参数来初始化对象的成员变量
· 为该类提供getter方法,但不要提供setter方法
· 如果有必要重写Object 类的hashCode和equals方法
下面的程序试图定义一个不可变类(Person),但因为包含一个引用类型的成员,且这个引用是可变类,所以导致了Person类也变成了可变类:
/**
* 一个失败的不可变类
* @author 《疯狂的Java讲义精粹》
*/
class Name{
private String firstName;
private String lastName;
//构造方法
public Name(){}
public Name(String firstName, String lastName){
this.firstName = firstName;
this.lastName = lastName;
}
//getter and setter
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;
}
}
public class Person {
private final Name name;
//构造方法
public Person(Name name){
this.name = name;
}
//只提供getter方法
public Name getName(){
return name;
}
public static void main(String[] args) {
Name n = new Name("Cocoon","Fan");
Person p = new Person(n);
//将输出CocoonFanFan
System.out.println(p.getName().getFirstName()+p.getName().getLastName());
//这时候依然可以改变Person对象的属性所指向的内容值,只是不能改变Person对象的属性
//p.name = null;//这里编译不能通过
p.getName().setFirstName("我的名字已经被改了!");
//将输出:我的名字已经被改了!Fan
System.out.println(p.getName().getFirstName() + p.getName().getLastName());
}
}
很明显,Person对象的name的firstName已经被改了,这不符合要求创建一个不可变类的初衷。
下面对Person类做一些修改,使其满足不可变类的要求:
/**
* 修改后的Person类满足不可变类的要求
* @author 《疯狂的Java讲义精粹》
*
*/
class Name{
private String firstName;
private String lastName;
//构造方法
public Name(){}
public Name(String firstName, String lastName){
this.firstName = firstName;
this.lastName = lastName;
}
//getter and setter
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;
}
}
public class Person {
private final Name name;
public Person (Name name){
//设置name 为临时创建的Name 对象,该对象的firstName 和 lastName与
//传入的name对象的firstName 和 lastName相同
this.name = new Name(name.getFirstName(), name.getLastName());
}
public Name getName(){
//返回一个匿名对象,该对象的firstName 和 lastName 与该对象里的name 的firstName
//和lastName相同
return new Name(name.getFirstName(),name.getLastName());
}
public static void main(String[] args) {
Name n = new Name("Cocoon","Fan");
Person p = new Person(n);
//将输出: CocoonFanFan
System.out.println(p.getName().getFirstName()+p.getName().getLastName());
//这时候改变不了Person对象的属性了
p.getName().setFirstName("我的名字已经被改了!");
//将输出:CocoonFan
System.out.println(p.getName().getFirstName() + p.getName().getLastName());
}
}
不可变类的实例状态不可改变,可以很方便的、地被多个对象共享。如果程序要使用相同的不可变类实例,则应考虑缓存这种不可变类的实例,因为重复创建相同的对象对系统的开销会很大。如果可能,应将已经创建的不可变类的实例进行缓存。
本例将使用一个数组来作为缓存池,从而实现一个缓存实例的不可变类:
/**
* 缓存实例不可变类
* @author 《疯狂Java讲义精粹》
* @date 3/7/2013
*/
class CacheImmutale {
private static int MAX_SIZE = 10;//缓存的大小
//穿件缓存池
private static CacheImmutale[] cache = new CacheImmutale[MAX_SIZE];
//记录缓存实例在缓存中的位置
private static int pos = 0;
private final String name;
private CacheImmutale(String name){
this.name = name;
}
public String getName(){
return name;
}
public static CacheImmutale valueOf(String name){
//遍历缓存对象
for (int i = 0; i < MAX_SIZE; i++) {
//如果已经存在相同的缓存实例,直接返回该缓存实例
if(cache[i] != null && cache[i].getName().equals(name)){
return cache[i];
}
}
//如果缓存池已满
if(pos == MAX_SIZE){
//把缓存的第一个对象覆盖
cache[0] = new CacheImmutale(name);
pos = 1;
} else {
//把新创建的对象缓存起来
cache[pos++] = new CacheImmutale(name);
}
return cache[pos-1];
}
//重写equals方法
public boolean equals(Object obj){
if(this == obj){
return true;
}
if(obj != null && obj.getClass() == CacheImmutale.class){
CacheImmutale c = (CacheImmutale)obj;
return name.equals(c.getName());
}
return false;
}
//重写hashCode 方法
public int hashCode(){
return name.hashCode();
}
}
public class TestCacheImmutale{
public static void main(String []args){
CacheImmutale c1 = CacheImmutale.valueOf("Hello");
CacheImmutale c2 = CacheImmutale.valueOf("Hello");
//将输出true
System.out.println("c1 == c2:" + (c1==c2));
}
}
Java提供的java.lang.Integer类,就采用了与上面CacheImmutable类类似的处理策略,如果采用new 构造器来创建Integer对象,则每次返回全新的Integer对象,如果采用valueOf()方法来创建对象,则会缓存该方法创建的对象。应该注意的是Integer只是缓存-128~127之间的Integer 对象,例如两次通过Integer.valueOf(200);方法生成的对象就是两个不同的对象。