天天看点

原型模式详解及深克隆浅克隆原理

原型模式(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 方法中返回单例对象即可 。

继续阅读