设计模式-原型模式
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的创建实际就是两种:单例模式和原型模式。(原型模式需要和工厂模式搭配起来)
如果需要引用或者转载博文,请在您的文章显目位置添加本博文链接及作者,谢谢!