Java原型模式(prototype)

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


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的引用是同一个

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

2019021818451036.png

深度克隆

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

实现的效果是:

20190218184847496.png

深度克隆(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

小结:通过clone的方式在获取大量对象的时候性能开销基本没有什么影响,而new的方式随着实例的对象越来越多,性能会急剧下降,所以原型模式是一种比较重要的获取实例的方式。大家应该掌握好。

开发中的应用场景

  原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。

• spring中bean的创建实际就是两种:单例模式和原型模式。(原型模式需要和工厂模式搭配起来)

相关文章
|
7月前
|
设计模式 安全 Java
面向对象编程的精髓:Java设计模式 - 原型模式(Prototype)完全参考手册
【4月更文挑战第7天】原型模式是OOP中的创建型设计模式,用于通过复制现有实例创建新实例,尤其适用于创建成本高或依赖其他对象的情况。它包括Prototype接口、ConcretePrototype实现和Client客户端角色。优点是性能优化、避免子类化和动态增加产品族。实现包括定义原型接口、实现具体原型和客户端调用克隆方法。最佳实践涉及确保克隆正确性、选择深拷贝或浅拷贝及考虑线程安全。但需注意克隆方法管理、性能开销和循环引用等问题。在Java中,实现Cloneable接口和覆盖clone方法可实现原型模式。
77 4
|
7月前
|
设计模式 Java
Java设计模式【五】:原型模式
Java设计模式【五】:原型模式
35 0
|
设计模式 存储 Java
JAVA设计模式4:谈谈原型模式在JAVA实战开发中的应用
JAVA设计模式4:谈谈原型模式在JAVA实战开发中的应用
163 1
|
3月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑】设计模式——原型模式
对比原型模式和传统方式的实现思路、代码方案、优缺点,阐述原型模式的使用场景,以及深拷贝、浅拷贝等相关概念,并扩展原型模式在Spring源码中的应用。
【Java笔记+踩坑】设计模式——原型模式
|
3月前
|
设计模式 Java
Java设计模式-原型模式(3)
Java设计模式-原型模式(3)
Java设计模式-原型模式(3)
|
6月前
|
设计模式 Java
Java设计模式之原型模式详解
Java设计模式之原型模式详解
|
7月前
|
设计模式 Java
23种设计模式,原型模式的概念优缺点以及JAVA代码举例
【4月更文挑战第10天】原型模式是一种创建型设计模式,它允许通过复制现有对象来创建新的对象,而无需知道如何创建的细节。这种模式的核心思想是基于一个原型实例,通过复制这个原型来创建新的对象
54 7
|
7月前
|
设计模式 Java Go
[设计模式Java实现附plantuml源码~创建型] 对象的克隆~原型模式
[设计模式Java实现附plantuml源码~创建型] 对象的克隆~原型模式
|
7月前
|
设计模式 存储 JSON
Java设计模式-原型模式
原型模式也是创建对象的一种方式,它一般用在这样的场景:系统中存在大量相同或相似对象的创建问题,如果用传统的构造函数来创建对象,会比较复杂而且耗费资源。这个时候使用原型模式的克隆方式,能够节省不少时间。比如Java 类中提供的`Object clone()`就是原型模式的应用。
50 1
Java设计模式-原型模式
|
设计模式 Java
创造与复制:揭秘Java设计模式中的原型模式
在软件开发领域,设计模式是解决常见问题的经验总结,提供了一些被验证过的指导原则。原型模式是创建型设计模式之一,它允许我们通过复制现有对象来创建新对象。在本文中,我们将探索原型模式的本质,以及它在Java中的实际应用。
76 0
下一篇
无影云桌面