天天看點

原型模式之深克隆和淺克隆實作

1 什麼是原型模式?

原型模式是用原型執行個體指定建立對象的種類,并且通過拷貝這些原型建立新的對象。

2 為什麼要用?

1 如果建立行的對象成本比較大,可以通過複制的方式減少建立的成本。

2 一個類的資訊很多,但是又需要一個新的類,新的類和以前的類不同的地方很少,此時可以使用原型模式進行複制操作得到新的對象,再對新對象改變一些資訊即可。

3 其他

3 怎麼用?

在Object類中提供了clone()方法,當簡單的重寫該方法時,由于該方法是protected的,是以隻能是同包下的類才能通路,修飾符可以改成public的,讓所有類可以進行克隆操作。

protected native Object clone() throws CloneNotSupportedException;
           

如果隻重寫了clone()方法,此時别的類在調用clone()方法,會在運作時出現運作時異常CloneNotSupportedException,

原型模式之深克隆和淺克隆實作

克隆類還需要再實作Cloneable接口,這是一個辨別接口,實作了這個接口的類JVM才會知道類是支援克隆操作的。

public interface Cloneable {}
           

淺克隆的情況

要進行克隆的類實作Cloneable接口,重寫Object類繼承來的clone()方法

/**
 * 房子類
 */
  public class House implements Cloneable {

    // 住房編号
    String roomId;

    // 住房面積
    int area;

    // 房子價值
    double price;

    // 房主
    Person owner;
 
    public House() {

    }
  
    public House(String roomId, int area, double price, Person owner) {
        this.roomId = roomId;
        this.area = area;
        this.price = price;
        this.owner = owner;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}



/**
 * 房主類
 */
  public class Person implements Cloneable {

    // 身份證号
    int id;

    // 姓名
    String name;

    public Person() {
    }

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}



/**
 * 隻支援淺克隆
 */
  public class Client {

    public static void main(String[] args) throws CloneNotSupportedException {

        Person MrLiu = new Person(1, "Mr.Liu");

        House h1 = new House("1302", 78, 2900, MrLiu);
        House h2 = (House) h1.clone();

        System.out.println(h1.owner == h2.owner);
    }
}
           
原型模式之深克隆和淺克隆實作

h1和h2不是同一個對象,但是h1的owner和h2的owner是同一個對象,表明是淺複制(淺克隆,即對象裡的引用對象隻拷貝引用,而不拷貝引用指向的對象,基本類型會拷貝)。

自定義clone過程實作深克隆

将上面的House類的clone()重寫成自己實作克隆

/**
 * 嵌套的使用克隆,如果類的對象屬性很多,都需要手動編碼實作,這個讓我很難受。
 * @return
 */
@Override
protected House clone() {
    House house = null;
    try {
        // 先以淺複制的形式複制house,
        // 此時house裡的owner屬性複制的引用而沒有複制對象
        house = (House) super.clone();
        // 再以淺複制的形式複制owner
        house.owner = (Person) house.owner.clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    return house;
}
           
原型模式之深克隆和淺克隆實作

Debug後可以看出,h1和h2不是同一個對象,h1的owner和h2的owner也不是同一個對象,是深克隆。

序列化實作深克隆

此處隻給出如何實作驗證這個克隆方案,具體如何實作序列化見其他優秀部落格,寫的很詳細。

Serializable也是一個辨別接口,表明實作該接口的類可以進行序列化。

public interface Serializable { }
           

這裡要注意的是House類實作了序列化,House類裡的引用類型也要實作序列化,要不然還是會出現序列化錯誤。

House類實作序列化,而Person類沒有實作序列化接口,運作時報NotSerializableException異常。

原型模式之深克隆和淺克隆實作
/**
 * 房子類
 */
public class House implements Serializable {
    // 住房編号
    String roomId;
    // 住房面積
    int area;
    // 房子價值
    double price;
    // 房主
    Person owner;

    public House() {
    }

    public House(String roomId, int area, double price, Person owner) {
        this.roomId = roomId;
        this.area = area;
        this.price = price;
        this.owner = owner;
    }
}


/**
 * 房主類
 */
public class Person implements Serializable {

    // 身份證号
    int id;
    // 姓名
    String name;

    public Person() {
    }

    public Person(int id, String name) {
        this.id = id;
        this.name = name;
    }
}


/**
 * 序列化的方式實作深克隆
 */
public class Client {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person MrLiu = new Person(1, "Mr.Liu");

        House h1 = new House("1302", 78, 2900, MrLiu);

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("E:\\test\\aaa")));
        oos.writeObject(h1);
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("E:\\test\\aaa")));

        House h2 = (House) ois.readObject();
        System.out.println(h2);
        ois.close();
    }

}
           
原型模式之深克隆和淺克隆實作

Debug後可以看出,h1和h2不是同一個對象,h1的owner和h2的owner也不是同一個對象,實作了深克隆。

4 什麼時候用?

參考第2條,什麼時候要用

5 如何優化?

能力有限,未能深入到這步,請體諒。

6 業務中有其他更好的替代方式嗎?

能力有限,未能深入到這步,請體諒。

繼續閱讀