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

原型模式(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;
/**
* Created by Tom.
*/
public class Monkey {
  public int height;
  public int weight;
  public Date birthday;
}
创建引用对象金箍棒Jingubang 类:

import java.io.Serializable;
/**
* Created by Tom.
*/
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 方法中返回单例对象即可 。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值