天天看點

原型模式詳解及深克隆淺克隆原理

原型模式(Prototype Pattern)是指原型執行個體指定建立對象的種類,并且通過拷貝這些

原型建立新的對象。

原型模式主要适用于以下場景:

1、類初始化消耗資源較多。

2、new 産生的一個對象需要非常繁瑣的過程(資料準備、通路權限等)

原型模式詳解及深克隆淺克隆原理

簡單克隆

一個标準的原型模式代碼,應該是這樣設計的。先建立原型Prototype 接口:

public interface Prototype{

    Prototype clone();

}

建立具體需要克隆的對象ConcretePrototype

import java.util.List;

public class ConcretePrototypeA implements Prototype {

    private int age;

   private String name;

  private List hobbies;

  public int getAge() {

    return age;

  }

  public void setAge(int age) {

     this.age = age;

  }

  public String getName() {

    return name;

 } 

public void setName(String name) {

this.name = name;

}

public List getHobbies() {

return hobbies;

}

public void setHobbies(List hobbies) {

this.hobbies = hobbies;

}

@Override

   public ConcretePrototypeA clone() {

    ConcretePrototypeA concretePrototype = new ConcretePrototypeA();

    concretePrototype.setAge(this.age);

    concretePrototype.setName(this.name);

    concretePrototype.setHobbies(this.hobbies);

    return concretePrototype;

   }

}

建立Client 對象

public class Client {

   private Prototype prototype;

    public Client(Prototype prototype){

   this.prototype = prototype;

    }

public Prototype startClone(Prototype concretePrototype){

    return (Prototype)concretePrototype.clone();

    }

}

測試代碼:

import java.util.ArrayList;

import java.util.List;

public class PrototypeTest {

  public static void main(String[] args) {

  // 建立一個具體的需要克隆的對象

  ConcretePrototypeA concretePrototype = new ConcretePrototypeA();

  // 填充屬性,友善測試

  concretePrototype.setAge(18);

  concretePrototype.setName("prototype");

  List hobbies = new ArrayList<String>();

  concretePrototype.setHobbies(hobbies);

  System.out.println(concretePrototype);

// 建立Client 對象,準備開始克隆

Client client = new Client(concretePrototype);

ConcretePrototypeA concretePrototypeClone = (ConcretePrototypeA)

client.startClone(concretePrototype);

System.out.println(concretePrototypeClone);

System.out.println("克隆對象中的引用類型位址值:" + concretePrototypeClone.getHobbies());

System.out.println("原對象中的引用類型位址值:" + concretePrototype.getHobbies());

System.out.println("對象位址比較:"+(concretePrototypeClone.getHobbies() ==

concretePrototype.getHobbies()));

   }

}

原型模式詳解及深克隆淺克隆原理

從測試結果看出hobbies 的引用位址是相同的,意味着複制的不是值,而是引用的位址。

這樣的話, 如果我們修改任意一個對象中的屬性值, concretePrototype 和

concretePrototypeCone 的hobbies 值都會改變。這就是我們常說的淺克隆。隻是完整

複制了值類型資料,沒有指派引用對象。換言之,所有的引用對象仍然指向原來的對象,

顯然不是我們想要的結果。下面我們來看深度克隆繼續改造。

深度克隆

我們換一個場景,大家都知道齊天大聖。首先它是一隻猴子,有七十二般變化,把一根

毫毛就可以吹出千萬個潑猴,手裡還拿着金箍棒,金箍棒可以變大變小。這就是我們耳

熟能詳的原型模式的經典展現。

建立原型猴子Monkey 類:

import java.util.Date;

public class Monkey {

  public int height;

  public int weight;

  public Date birthday;

}

建立引用對象金箍棒Jingubang 類:

import java.io.Serializable;

public class JinGuBang implements Serializable {

  public float h = 100;

  public float d = 10;

  public void big(){

    this.d *= 2;

    this.h *= 2;

 }

  public void small(){

    this.d /= 2;

   this.h /= 2;

}

}

建立具體的對象齊天大聖QiTianDaSheng 類:

import java.io.*;

import java.util.Date;

public class QiTianDaSheng extends Monkey implements Cloneable,Serializable {

   public JinGuBang jinGuBang;

   public QiTianDaSheng(){

   //隻是初始化

  this.birthday = new Date();

  this.jinGuBang = new JinGuBang();

}

  @Override

  protected Object clone() throws CloneNotSupportedException {

   return this.deepClone();

     }

  public Object deepClone(){

    try{

      ByteArrayOutputStream bos = new ByteArrayOutputStream();

     ObjectOutputStream oos = new ObjectOutputStream(bos);

     oos.writeObject(this);

    ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());

    ObjectInputStream ois = new ObjectInputStream(bis);

    QiTianDaSheng copy = (QiTianDaSheng)ois.readObject();

    copy.birthday = new Date();

   return copy;

   }catch (Exception e){

    e.printStackTrace();

      return null;

    }

}

public QiTianDaSheng shallowClone(QiTianDaSheng target){

    QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();

    qiTianDaSheng.height = target.height;

    qiTianDaSheng.weight = target.height;

   qiTianDaSheng.jinGuBang = target.jinGuBang;

    qiTianDaSheng.birthday = new Date();

      return qiTianDaSheng;

     }

}

測試代碼:

public class DeepCloneTest {

   public static void main(String[] args) {

   QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();

    try {

    QiTianDaSheng clone = (QiTianDaSheng)qiTianDaSheng.clone();

   System.out.println("深克隆:" + (qiTianDaSheng.jinGuBang == clone.jinGuBang));

   } catch (Exception e) {

    e.printStackTrace();

   }

   QiTianDaSheng q = new QiTianDaSheng();

   QiTianDaSheng n = q.shallowClone(q);

   System.out.println("淺克隆:" + (q.jinGuBang == n.jinGuBang));

   }

}

原型模式詳解及深克隆淺克隆原理

運作結果:

克隆破壞單例模式

如果我們克隆的目标的對象是單例對象,那意味着,深克隆就會破壞單例。實際上防止

克隆破壞單例解決思路非常簡單,禁止深克隆便可。要麼你我們的單例類不實作

Cloneable 接口;要麼我們重寫clone()方法,在clone 方法中傳回單例對象即可 。

繼續閱讀