《疯狂Java讲义精粹》读书笔记8 ------ 不可变类

=======================《疯狂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);方法生成的对象就是两个不同的对象。

posted @ 2013-03-07 15:31  Cocoon  阅读(329)  评论(0编辑  收藏  举报