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);
    }
}

上一篇:守護阻塞

相關文章
相關標籤/搜索