天天看点

Java原型模式(prototype)

  prototype模式也就是原型模式,是javaGOF23种设计模式中的一种,我们在学习spring的时候在bean标签的学习中碰到过,所以本文来给大家介绍下原型模式

原型模式

  在java中我们知道通过new关键字创建的对象是非常繁琐的(类加载判断,内存分配,初始化等),在我们需要大量对象的情况下,原型模式就是我们可以考虑实现的方式。

  原型模式我们也称为克隆模式,即一个某个对象为原型克隆出来一个一模一样的对象,该对象的属性和原型对象一模一样。而且对于原型对象没有任何影响。原型模式的克隆方式有两种:浅克隆和深度克隆

原型模式 说明
浅克隆

只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,

还是指向原生对象的内部元素地址

深度克隆 深复制把要复制的对象所引用的对象都复制了一遍

浅克隆

被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象。 Object类提供的方法clone只是拷贝本对象 , 其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址

实现

被克隆的对象必须Cloneable,Serializable这两个接口

原型类

package com.dpb.prototype;

import java.io.Serializable;
import java.util.Date;

/**
 * 原型类:被克隆的类型
 * @author dengp
 *
 */
public class User implements Cloneable,Serializable{
  
  private String name;
  
  private Date birth;
  
  private int age;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Date getBirth() {
    return birth;
  }

  public void setBirth(Date birth) {
    this.birth = birth;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }
  
  /**
   * 实现克隆的方法
   */
  public Object clone() throws CloneNotSupportedException{
    return super.clone();
  }
}      

测试类

public static void main(String[] args) throws CloneNotSupportedException {
  Date date =  new Date(1231231231231l);
  User user = new User();
  user.setName("波波烤鸭");
  user.setAge(18);
  user.setBirth(date);
  System.out.println("----输出原型对象的属性------");
  System.out.println(user);
  System.out.println(user.getName());
  System.out.println(user.getBirth());
  // 克隆对象
  User user1 =(User) user.clone();
  // 修改原型对象中的属性
  date.setTime(123231231231l);
  System.out.println(user.getBirth());
  
  // 修改参数
  user1.setName("dpb");
  System.out.println("-------克隆对象的属性-----");
  System.out.println(user1);
  System.out.println(user1.getName());
  System.out.println(user1.getBirth());
}      

输出结果

com.dpb.prototype.User@15db9742
波波烤鸭
Tue Jan 06 16:40:31 CST 2009 # 1
Tue Nov 27 14:53:51 CST 1973 # 2
-------克隆对象的属性-----
com.dpb.prototype.User@5c647e05
dpb
Tue Nov 27 14:53:51 CST 1973 # 和2的结果一样 说明两个对象的Date的引用是同一个      

浅克隆的问题:虽然产生了两个完全不同的对象,但是被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。

Java原型模式(prototype)

深度克隆

被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。

实现的效果是:

Java原型模式(prototype)

深度克隆(deep clone)有两种实现方式,第一种是在浅克隆的基础上实现,第二种是通过序列化和反序列化实现,我们分别来介绍

第一种方式

在浅克隆的基础上实现

原型类:

package com.dpb.prototype;

import java.io.Serializable;
import java.util.Date;

/**
 * 原型类:被克隆的类型
 * 深度克隆测试
 * @author dengp
 *
 */
public class User2 implements Cloneable,Serializable{
  
  private String name;
  
  private Date birth;
  
  private int age;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Date getBirth() {
    return birth;
  }

  public void setBirth(Date birth) {
    this.birth = birth;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }
  
  /**
   * 实现克隆的方法
   * 深度克隆(deep clone)
   */
  public Object clone() throws CloneNotSupportedException{
    Object object = super.clone();
    // 实现深度克隆(deep clone)
    User2 user = (User2)object;
    user.birth = (Date) this.birth.clone();
    return object;
  }
}      

测试代码

public static void main(String[] args) throws CloneNotSupportedException {
  Date date =  new Date(1231231231231l);
  User2 user = new User2();
  user.setName("波波烤鸭");
  user.setAge(18);
  user.setBirth(date);
  System.out.println("----输出原型对象的属性------");
  System.out.println(user);
  System.out.println(user.getName());
  System.out.println(user.getBirth());
  // 克隆对象
  User2 user1 =(User2) user.clone();
  // 修改原型对象中的属性
  date.setTime(123231231231l);
  System.out.println(user.getBirth());
  
  // 修改参数
  user1.setName("dpb");
  System.out.println("-------克隆对象的属性-----");
  System.out.println(user1);
  System.out.println(user1.getName());
  System.out.println(user1.getBirth());
}      

输出结果:

com.dpb.prototype.User2@15db9742
波波烤鸭
Tue Jan 06 16:40:31 CST 2009 1
Tue Nov 27 14:53:51 CST 1973 2
-------克隆对象的属性-----
com.dpb.prototype.User2@5c647e05
dpb
Tue Jan 06 16:40:31 CST 2009      

我们发现克隆的对象的属性并没有随着我们对Date的修改而改变,说明克隆对象的Date属性和原型对象的Date属性引用的不是同一个对象,实现的深度复制。

第二种方式:序列化和反序列化

名称 说明
序列化 把对象转换为字节序列的过程。
反序列化 把字节序列恢复为对象的过程。
public static void main(String[] args) throws CloneNotSupportedException, Exception {
  Date date =  new Date(1231231231231l);
  User user = new User();
  user.setName("波波烤鸭");
  user.setAge(18);
  user.setBirth(date);
  System.out.println("-----原型对象的属性------");
  System.out.println(user);
  System.out.println(user.getName());
  System.out.println(user.getBirth());
  
  //使用序列化和反序列化实现深复制
  ByteArrayOutputStream bos = new ByteArrayOutputStream();
  ObjectOutputStream    oos = new ObjectOutputStream(bos);
  oos.writeObject(user);
  byte[] bytes = bos.toByteArray();
  
  ByteArrayInputStream  bis = new ByteArrayInputStream(bytes);
  ObjectInputStream   ois = new ObjectInputStream(bis);
  
  //克隆好的对象!
  User user1 = (User) ois.readObject();   
  
  // 修改原型对象的值
  date.setTime(221321321321321l);
  System.out.println(user.getBirth());
  
  System.out.println("------克隆对象的属性-------");
  System.out.println(user1);
  System.out.println(user1.getName());
  System.out.println(user1.getBirth());
}      

输出结果

-----原型对象的属性------
com.dpb.prototype.User@15db9742
波波烤鸭
Tue Jan 06 16:40:31 CST 2009
Sat May 24 16:48:41 CST 8983
------克隆对象的属性-------
com.dpb.prototype.User@7cca494b
波波烤鸭
Tue Jan 06 16:40:31 CST 2009      

实现了和第一种实现方式相同的效果~实现了深度克隆

原型模式和直接new对象方式的比较

当我们需要大量的同一类型对象的时候可以使用原型模式,下面是两种方式的性能对比:

用两种方式同时生成10个对象

/**
 * 测试普通new方式创建对象和clone方式创建对象的效率差异!
 * 如果需要短时间创建大量对象,并且new的过程比较耗时。则可以考虑使用原型模式!
 * @author 波波烤鸭
 *
 */
public class Client4 {
  
  public static void testNew(int size){
    long start = System.currentTimeMillis();
    for(int i=0;i<size;i++){
      User t = new User();
    }
    long end = System.currentTimeMillis();
    System.out.println("new的方式创建耗时:"+(end-start));
  }
  
  public static void testClone(int size) throws CloneNotSupportedException{
    long start = System.currentTimeMillis();
    User t = new User();
    for(int i=0;i<size;i++){
      User temp = (User) t.clone();
    }
    long end = System.currentTimeMillis();
    System.out.println("clone的方式创建耗时:"+(end-start));
  }
  
  
  public static void main(String[] args) throws Exception { 
    testNew(10);
    testClone(10);
  }
}


class User implements Cloneable {  //用户
  public User() {
    try {
      Thread.sleep(10);  //模拟创建对象耗时的过程!
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
  
  @Override
  protected Object clone() throws CloneNotSupportedException {
    Object obj = super.clone();  //直接调用object对象的clone()方法!
    return obj;
  }
}      

输出结果:

new的方式创建耗时:108
clone的方式创建耗时:11      

用两种方式同时生成1000个对象

/**
 * 测试普通new方式创建对象和clone方式创建对象的效率差异!
 * 如果需要短时间创建大量对象,并且new的过程比较耗时。则可以考虑使用原型模式!
 * @author 波波烤鸭
 *
 */
public class Client4 {
  
  public static void testNew(int size){
    long start = System.currentTimeMillis();
    for(int i=0;i<size;i++){
      User t = new User();
    }
    long end = System.currentTimeMillis();
    System.out.println("new的方式创建耗时:"+(end-start));
  }
  
  public static void testClone(int size) throws CloneNotSupportedException{
    long start = System.currentTimeMillis();
    User t = new User();
    for(int i=0;i<size;i++){
      User temp = (User) t.clone();
    }
    long end = System.currentTimeMillis();
    System.out.println("clone的方式创建耗时:"+(end-start));
  }
  
  
  public static void main(String[] args) throws Exception { 
    testNew(1000);
    testClone(1000);
  }
}


class User implements Cloneable {  //用户
  public User() {
    try {
      Thread.sleep(10);  //模拟创建对象耗时的过程!
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
  
  @Override
  protected Object clone() throws CloneNotSupportedException {
    Object obj = super.clone();  //直接调用object对象的clone()方法!
    return obj;
  }
}      

输出结果

new的方式创建耗时:10836
clone的方式创建耗时:10      

开发中的应用场景

继续阅读