java不可变对象分析

  创建后状态不能被修改的对象成为不可变对象,每次对他们的改变都是产生了新的immutable的对象,实际上JDK本身就自带了一些immutable类,比如String,Integer以及其他包装类。为什么说String是immutable的呢?比如:java.lang.String 的trim,uppercase,substring等方法,它们返回的都是新的String对象,而并不是直接修改原来的对象。

  

要写出这样的类,需要遵循以下几个原则:

  1. immutable对象的状态在创建之后就不能发生改变,任何对它的改变都应该产生一个新的对象。
  2. Immutable类的所有的属性都应该是final的。
  3. 对象必须被正确的创建,比如:对象引用在对象创建过程中不能泄露(leak)。
  4. 对象应该是final的,以此来限制子类继承父类,以避免子类改变了父类的immutable特性。
  5. 不提供对成员的改变方法,例如:setXXXX 
  6. 确保所有的方法不会被重载。手段有两种:使用final Class(强不可变类),或者将所有类方法加上final(弱不可变类)。如果某一个类成员不是原始变量(primitive)或者不可变类,必须通过在成员初始化(in)或者get方法(out)时通过深度clone方法,来确保类的不可变

当然不完全遵守上面的原则也能够创建immutable的类,比如String的hashcode就不是final的,但它能保证每次调用它的值都是一致的,无论你多少次计算这个值,它都是一致的,因为这些值的是通过计算final的属性得来的!

另外,如果你的Java类中存在很多可选的和强制性的字段,你也可以使用建造者模式来创建一个immutable的类。

下面是一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public final class Contacts {
 
    private final String name;
    private final String mobile;
 
    public Contacts(String name, String mobile) {
        this.name = name;
        this.mobile = mobile;
    }
   
    public String getName(){
        return name;
    }
   
    public String getMobile(){
        return mobile;
    }
}

我们为类添加了final修饰,从而避免因为继承和多态引起的immutable风险。

上面是最简单的一种实现immutable类的方式,可以看到它的所有属性都是final的。

有时候你要实现的immutable类中可能包含mutable的类,比如java.util.Date,尽管你将其设置成了final的,但是它的值还是可以被修改的,为了避免这个问题,我们建议返回给用户该对象的一个拷贝,这也是Java的最佳实践之一。下面是一个创建包含mutable类对象的immutable类的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public final class ImmutableReminder{
    private final Date remindingDate;
   
    public ImmutableReminder (Date remindingDate) {
        if(remindingDate.getTime() < System.currentTimeMillis()){
            throw new IllegalArgumentException("Can not set reminder” +
                        for past time: " + remindingDate);
        }
        this.remindingDate = new Date(remindingDate.getTime());
    }
   
    public Date getRemindingDate() {
        return (Date) remindingDate.clone();
    }
}

上面的getRemindingDate()方法可以看到,返回给用户的是类中的remindingDate属性的一个拷贝,这样的话如果别人通过getRemindingDate()方法获得了一个Date对象,然后修改了这个Date对象的值,那么这个值的修改将不会导致ImmutableReminder类对象中remindingDate值的修改。

posted @ 2014-08-04 11:54  程序猿进化之路  阅读(2065)  评论(1)    收藏  举报