如果一个对象在构造之后不可改变,那么这个对象就是不可变对象。不可变对象被广泛认同的作用是建立简单、可靠代码的万全之策。
不可变对象在并发应用程序中特别有用,因为他们不能被修改,他们不能发生线程冲突,或者发生不一致状态的读取。
程序员经常不心甘情愿的使用不可变对象,因为他们不能修改这个对象,只能new一个对象。创建对象的开销一般是被评估过高的,可以和不可能变对象相关的效率问题抵消。包括,垃圾回收的开销和the elimination of code needed to protect mutable objects from corruption。
下面给出一个可变对象的类,从这个例子引导出不可变对象。这样可以对比出不可变对象的优势。
(一) 同步类的例子
类SynchronizedRGB,是一个代表颜色的例子。每个对象的三个整数分别代表一个原色,然后一个string代表颜色名字。
public class SynchronizedRGB {
// Values must be between 0 and 255.
private int red;
private int green;
private int blue;
private String name;
private void check(int red, int green, int blue) {
if (red < 0 || red > 255
|| green < 0 || green > 255
|| blue < 0 || blue > 255) {
throw new IllegalArgumentException();
}
}
public SynchronizedRGB(int red, int green, int blue, String name) {
check(red, green, blue);
this.red = red;
this.green = green;
this.blue = blue;
this.name = name;
}
public void set(int red, int green, int blue, String name) {
check(red, green, blue);
synchronized (this) {
this.red = red;
this.green = green;
this.blue = blue;
this.name = name;
}
}
public synchronized int getRGB() {
return ((red << 16) | (green << 8) | blue);
}
public synchronized String getName() {
return name;
}
public synchronized void invert() {
red = 255 - red;
green = 255 - green;
blue = 255 - blue;
name = "Inverse of " + name;
}
}
SynchronizedRGB必须很小心的使用去避免不一致状态。假设,一个线程执行下面的代码:
SynchronizedRGB color =
new SynchronizedRGB(0, 0, 0, "Pitch Black");
...
int myColorInt = color.getRGB(); //Statement 1
String myColorName = color.getName(); //Statement 2
如果另一个线程在statement1和statement2之间调用coler.set,myColerInt和myColorName将不一致。为了避免这情况发生,两个语句必须绑定在一起:
synchronized (color) {
int myColorInt = color.getRGB();
String myColorName = color.getName();
}
这种情况只可能发生在可变对象,对于不可变对象版本的SynchronizedRGB这不是一个问题。
(二) 定义不可变对象的策略
接下来的规则定义了创建不可变对象的简单策略。并非所有的“不可改变的”类要遵循这些规则。这并不是说这些类的创建者是草率的——他们有很好的理由相信他们创建的类构造之后不会被改变。但是这要做复杂的分析,不适合初学者。
1,不要提供“setter”方法——那些修改域或者域关联的对象的方法。
2,让所有域都是final和private
3,不允许子类覆盖方法。最简单的方法是把class定义成final。更复杂的方法是把构造方法定义成private,然后通过工程方法构建实例。
4,如果对象的域引用了可变对象,不允许这些对象改变:?不提供修改这些对象的方法。
?不要共享可变对象的引用。不要通过构造方法存储可变对象的引用;如果有必要,创建对象副本,存储副本的引用。同样,避免在方法中返回原对象,而是创建你的内部可变对象的副本。
按照以下步骤给SynchronizedRGB添加这个策略:
1,class有两个setter方法。第一个set方法,任意转换对象,在不可变版本里去掉。第二个invert方法,用创建一个新对象代替修改这个对象。
2,所有的域是private和final。
3,class被声明成final
4,只有一个域引用对象,这个对象是他自己。因此,不用考虑被包含的可变对象的改变。
做了这些,我们得到ImmutableRGB:
final public class ImmutableRGB {
// Values must be between 0 and 255.
final private int red;
final private int green;
final private int blue;
final private String name;
private void check(int red, int green, int blue) {
if (red < 0 || red > 255|| green < 0 || green > 255|| blue < 0 || blue > 255) {
throw new IllegalArgumentException();
}
}
public ImmutableRGB(int red, int green, int blue, String name) {
check(red, green, blue);
this.red = red;
this.green = green;
this.blue = blue;
this.name = name;
}
public int getRGB() {
return ((red << 16) | (green << 8) | blue);
}
public String getName() {
return name;
}
public ImmutableRGB invert() {
return new ImmutableRGB(255 - red, 255 - green, 255 - blue, "Inverse of " + name);
}
}