Java™ 教程(不可变对象)

不可变对象

若是一个对象的状态在构造后不能改变,则该对象被认为是不可变的,对不可变对象的最大依赖被普遍认为是一种建立简单、可靠代码的合理策略。java

不可变对象在并发应用程序中特别有用,因为它们不能改变状态,所以它们不会被线程干扰破坏或在不一致的状态下观察。git

程序员一般不肯意使用不可变对象,由于他们担忧建立新对象的成本而不是就地更新对象的成本,对象建立的影响常常被高估,而且能够经过与不可变对象相关联的一些效率来抵消,这些包括因为垃圾收集而减小的开销,以及消除保护可变对象免于损坏所需的代码。程序员

如下小节采用其实例可变的类,并从中派生出具备不可变实例的类,经过这样作,它们为这种转换提供了通常规则,并演示了不可变对象的一些优势。github

同步类示例

SynchronizedRGB类定义了表示颜色的对象,每一个对象将颜色表示为表明主要颜色值的三个整数和一个给出颜色名称的字符串。segmentfault

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

若是另外一个线程在语句1以后但在语句2以前调用color.set,则myColorInt的值将与myColorName的值不匹配,为了不这种结果,必须将两个语句绑定在一块儿:函数

synchronized (color) {
    int myColorInt = color.getRGB();
    String myColorName = color.getName();
}

这种不一致只适用于可变对象 — 对于不可变版本的SynchronizedRGB,它不会是一个问题。this

一种定义不可变对象的策略

如下规则定义了用于建立不可变对象的简单策略,并不是全部记录为“不可变”的类都遵循这些规则。这并不必定意味着这些类的创造者是草率的 — 他们可能有充分的理由相信他们类的实例在构造后永远不会改变,可是,这种策略须要复杂的分析,不适合初学者。线程

  1. 不要提供“setter”方法 — 修改字段或字段引用的对象的方法。
  2. 使全部字段为finalprivate
  3. 不容许子类重写方法,最简单的方法是将类声明为final,更复杂的方法是使构造函数为private并在工厂方法中构造实例。
  4. 若是实例字段包含对可变对象的引用,则不容许更改这些对象:code

    • 不要提供修改可变对象的方法。
    • 不要共享对可变对象的引用,永远不要存储对传递给构造函数的外部可变对象的引用,若有必要,建立副本并存储对副本的引用,一样,必要时建立内部可变对象的副本,以免在方法中返回原始对象。

将此策略应用于SynchronizedRGB会致使如下步骤:

  1. 这个类中有两个setter方法,第一个方法set,任意改变对象,在类的不可变版本中不存在,第二个方法invert,能够经过让它建立一个新对象而不是修改现有对象来进行调整。
  2. 全部字段都已为private,他们进一步得到final
  3. 该类自己被声明为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);
    }
}

上一篇:守护阻塞

相关文章
相关标签/搜索