设计模式-原型模式

 

Java原型模式(prototype)

Java原型模式(prototype)

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

原型模式

  在java中我们知道通过new关键字创建的对象是非常繁琐的(类加载判断,内存分配,初始化等),在我们需要大量对象的情况下,原型模式就是我们可以考虑实现的方式。  &emps;原型模式我们也称为克隆模式,即一个某个对象为原型克隆出来一个一模一样的对象,该对象的属性和原型对象一模一样。而且对于原型对象没有任何影响。原型模式的克隆方式有两种:浅克隆和深度克隆
 
原型模式
说明
浅克隆
只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址
深度克隆
深复制把要复制的对象所引用的对象都复制了一遍
 

浅克隆

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

实现

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

输出结果
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的引用是同一个
浅克隆的问题:虽然产生了两个完全不同的对象,但是被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。

深度克隆

被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍。 实现的效果是:
深度克隆(deep clone)有两种实现方式,第一种是在浅克隆的基础上实现,第二种是通过序列化和反序列化实现,我们分别来介绍

第一种方式

在浅克隆的基础上实现 原型类:
 1 package com.dpb.prototype;
 2  
 3 import java.io.Serializable;
 4 import java.util.Date;
 5  
 6 /**
 7 * 原型类:被克隆的类型
 8 * 深度克隆测试
 9 * @author dengp
10 *
11 */
12 public class User2 implements Cloneable,Serializable{
13     
14     private String name;
15     
16     private Date birth;
17     
18     private int age;
19  
20     public String getName() {
21         return name;
22     }
23  
24     public void setName(String name) {
25         this.name = name;
26     }
27  
28     public Date getBirth() {
29         return birth;
30     }
31  
32     public void setBirth(Date birth) {
33         this.birth = birth;
34     }
35  
36     public int getAge() {
37         return age;
38     }
39  
40     public void setAge(int age) {
41         this.age = age;
42     }
43     
44     /**
45      * 实现克隆的方法
46      * 深度克隆(deep clone)
47      */
48     public Object clone() throws CloneNotSupportedException{
49         Object object = super.clone();
50         // 实现深度克隆(deep clone)
51         User2 user = (User2)object;
52         user.birth = (Date) this.birth.clone();
53         return object;
54     }
55 }

测试代码

 

 1 public static void main(String[] args) throws CloneNotSupportedException {
 2     Date date = new Date(1231231231231l);
 3     User2 user = new User2();
 4     user.setName("波波烤鸭");
 5     user.setAge(18);
 6     user.setBirth(date);
 7     System.out.println("----输出原型对象的属性------");
 8     System.out.println(user);
 9     System.out.println(user.getName());
10     System.out.println(user.getBirth());
11     // 克隆对象
12     User2 user1 =(User2) user.clone();
13     // 修改原型对象中的属性
14     date.setTime(123231231231l);
15     System.out.println(user.getBirth());
16     
17     // 修改参数
18     user1.setName("dpb");
19     System.out.println("-------克隆对象的属性-----");
20     System.out.println(user1);
21     System.out.println(user1.getName());
22     System.out.println(user1.getBirth());
23 }

 

 

 

输出结果:
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属性引用的不是同一个对象,实现的深度复制。

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

 
名称
说明
序列化
把对象转换为字节序列的过程。
反序列化
把字节序列恢复为对象的过程。
 
 1 public static void main(String[] args) throws CloneNotSupportedException, Exception {
 2     Date date = new Date(1231231231231l);
 3     User user = new User();
 4     user.setName("波波烤鸭");
 5     user.setAge(18);
 6     user.setBirth(date);
 7     System.out.println("-----原型对象的属性------");
 8     System.out.println(user);
 9     System.out.println(user.getName());
10     System.out.println(user.getBirth());
11     
12     //使用序列化和反序列化实现深复制
13     ByteArrayOutputStream bos = new ByteArrayOutputStream();
14     ObjectOutputStream oos = new ObjectOutputStream(bos);
15     oos.writeObject(user);
16     byte[] bytes = bos.toByteArray();
17     
18     ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
19     ObjectInputStream     ois = new ObjectInputStream(bis);
20     
21     //克隆好的对象!
22     User user1 = (User) ois.readObject();
23     
24     // 修改原型对象的值
25     date.setTime(221321321321321l);
26     System.out.println(user.getBirth());
27     
28     System.out.println("------克隆对象的属性-------");
29     System.out.println(user1);
30     System.out.println(user1.getName());
31     System.out.println(user1.getBirth());
32 }

 

输出结果
-----原型对象的属性------
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个对象
 1 /**
 2 * 测试普通new方式创建对象和clone方式创建对象的效率差异!
 3 * 如果需要短时间创建大量对象,并且new的过程比较耗时。则可以考虑使用原型模式!
 4 * @author 波波烤鸭
 5 *
 6 */
 7 public class Client4 {
 8     
 9     public static void testNew(int size){
10         long start = System.currentTimeMillis();
11         for(int i=0;i<size;i++){
12             User t = new User();
13         }
14         long end = System.currentTimeMillis();
15         System.out.println("new的方式创建耗时:"+(end-start));
16     }
17     
18     public static void testClone(int size) throws CloneNotSupportedException{
19         long start = System.currentTimeMillis();
20         User t = new User();
21         for(int i=0;i<size;i++){
22             User temp = (User) t.clone();
23         }
24         long end = System.currentTimeMillis();
25         System.out.println("clone的方式创建耗时:"+(end-start));
26     }
27     
28     
29     public static void main(String[] args) throws Exception {    
30         testNew(10);
31         testClone(10);
32     }
33 }
34  
35  
36 class User implements Cloneable { //用户
37     public User() {
38         try {
39             Thread.sleep(10); //模拟创建对象耗时的过程!
40         } catch (InterruptedException e) {
41             e.printStackTrace();
42         }
43     }
44     
45     @Override
46     protected Object clone() throws CloneNotSupportedException {
47         Object obj = super.clone(); //直接调用object对象的clone()方法!
48         return obj;
49     }
50 }

 

输出结果:
new的方式创建耗时:108
clone的方式创建耗时:11

 

用两种方式同时生成1000个对象
 1 /**
 2 * 测试普通new方式创建对象和clone方式创建对象的效率差异!
 3 * 如果需要短时间创建大量对象,并且new的过程比较耗时。则可以考虑使用原型模式!
 4 * @author 波波烤鸭
 5 *
 6 */
 7 public class Client4 {
 8     
 9     public static void testNew(int size){
10         long start = System.currentTimeMillis();
11         for(int i=0;i<size;i++){
12             User t = new User();
13         }
14         long end = System.currentTimeMillis();
15         System.out.println("new的方式创建耗时:"+(end-start));
16     }
17     
18     public static void testClone(int size) throws CloneNotSupportedException{
19         long start = System.currentTimeMillis();
20         User t = new User();
21         for(int i=0;i<size;i++){
22             User temp = (User) t.clone();
23         }
24         long end = System.currentTimeMillis();
25         System.out.println("clone的方式创建耗时:"+(end-start));
26     }
27     
28     
29     public static void main(String[] args) throws Exception {    
30         testNew(1000);
31         testClone(1000);
32     }
33 }
34  
35  
36 class User implements Cloneable { //用户
37     public User() {
38         try {
39             Thread.sleep(10); //模拟创建对象耗时的过程!
40         } catch (InterruptedException e) {
41             e.printStackTrace();
42         }
43     }
44     
45     @Override
46     protected Object clone() throws CloneNotSupportedException {
47         Object obj = super.clone(); //直接调用object对象的clone()方法!
48         return obj;
49     }
50 }

输出结果

 

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

 

 

 

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

开发中的应用场景

  原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。
   spring中bean的创建实际就是两种:单例模式和原型模式。(原型模式需要和工厂模式搭配起来)
 
 
posted @ 2019-01-02 10:25  studyMore  阅读(148)  评论(0编辑  收藏  举报