- 浏览: 677817 次
- 性别:
- 来自: 深圳
最新评论
-
fingerplay:
请问一下,第一份,逻辑树,就是代码里可以操纵的,例如更改lay ...
UIView与CALayer -
ok_lyc:
分享到哪里去了
iPhoneUIFont各种字体 -
lliiqiang:
我的个人理解:wait方法是在java虚拟机层面上在获取对象锁 ...
JAVA多线程同步wait、notify、synchronized -
milixw:
谢谢分享,就在找这个
iphone 推送通知 java 实现 -
wsqwsq000:
你的log4j包不行,上网搜一下:log4j-1.2.16.j ...
iphone 推送通知 java 实现
5.1 概述
谈到原型模式,学过Java的人可能会想到java.lang.Cloneable这个接口,以为Java的原型模式描述的就是java.lang.Cloneable接口的使用,这就大错特错了。其实,原型模式在我们日常生活中经常可以看到,比如你刚给你的客厅做了装修,你朋友正好也希望给他的客厅做装修,那么,他可能会把你家的装修方案拿过来改改就成,你的装修方案就是原型。
由于很多OOP语言都支持对象的克隆(拷贝)以方便复制对象,但这些方式并不那么完美,后述我们将会讨论。
5.2 原型模式
当创建这些对象(一般情况是一些大对象)非常耗时,或者创建过程非常复杂时,非常有用,GoF给出的原型模式定义如下:
原型模式的静态类图非常简单,如下所示:
Client使用Prototype的clone()方法得到这个对象的拷贝,其实拷贝原型对象不一定是指从内存中进行拷贝,我们的原型数据可能保存在数据库里。
一般情况下,OOP语言都提供了内存中对象的复制,Java语言提供了对象的浅拷贝(Shallow copy),也就是说复制一个对象时,如果它的一个属性是引用,则复制这个引用,使之指向内存中同一个对象;但如果为此属性创建了一个新对象,让其引用指向它,即是深拷贝(Deep copy)。
5.3 寄个快递
下面是一个邮寄快递的场景:
“给我寄个快递。”顾客说。
“寄往什么地方?寄给……?”你问。
“和上次差不多一样,只是邮寄给另外一个地址,这里是邮寄地址……”顾客一边说一边把写有邮寄地址的纸条给你。
“好!”你愉快地答应,因为你保存了用户的以前邮寄信息,只要复制这些数据,然后通过简单的修改就可以快速地创建新的快递数据了。
5.4 实现
我们在复制新的数据时,需要特别注意的是,我们不能把所有数据都复制过来,例如,当对象包含主键时,不能使用原型数据的主键,必须创建一个新的主键。我们这里提供一个静态工厂方法,来获得原型数据,然后拷贝这些数据,最后做相应的初始化。为了操作安全起见,我们不直接使用原型数据,而是使用clone()方法从内存克隆这条原型数据做后续操作。我们使用Java提供java.lang.Cloneable接口克隆数据,它实现对象的浅拷贝,关于java.lang.Cloneable接口的使用请看后续介绍。
5.4.1 UML静态类图
Client使用PackageInfo提供的静态工厂方法clonePackage(String userName)创建一个新对象:首先根据userName加载一条用户以前的数据作为原型数据(可以是数据库,可以是其他任何你保存数据的地方),然后在内存中克隆这条数据,最后初始化该数据并返回。
5.4.2 代码实现
public class PackageInfo implements Cloneable {
//getters, setters and other methods...
public PackageInfo clone() {
try {
return (PackageInfo)super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("Cloning not allowed.");
return null;
}
}
public static PackageInfo clonePackage(String userName) {
//load package as prototype data from db...
PackageInfo prototype = loadPackageInfo(userName);
//clone information ...
prototype = prototype.clone();
//initialize copied data ...
prototype.setId(null);
return prototype;
}
}
代码注解:
- Java的java.lang.Object方法里就提供了克隆方法clone(),原则上似乎所有类都拥有此功能,但其实不然,关于它的使用有如下限制:
- 要实现克隆,必须实现java.lang.Cloneable接口,否则在运行时调用clone()方法,会抛CloneNotSupportedException异常。
- 返回的是Object类型的对象,所以使用时可能需要强制类型转换。
- 该方法是protected的,如果想让外部对象使用它,必须在子类重写该方法,设定其访问范围是public的,参见PackageInfo的clone()方法。
- Object的clone()方法的复制是采用逐字节的方式从复制内存数据,复制了属性的引用,而属性所指向的对象本身没有被复制,因此所复制的引用指向了相同的对象。由此可见,这种方式拷贝对象是浅拷贝,不是深拷贝。
- 静态工厂方法public static PackageInfo clonePackage(String userName)方法根据原型创建一份拷贝:首先拿出用户以前的一条数据,即这句PackageInfo prototype = loadPackageInfo(userName),然后调用方法它的clone()方法完成内存拷贝,即prototype.clone(),最后我们初始化这条新数据,比如使id为空等。
现在来看看我们的测试代码,如下所示:
public class PackageInfoTestDrive {
public static void main(String[] args) {
PackageInfo currentInfo = PackageInfo.clonePackage("John");
System.out.println("Original package information:");
display(currentInfo);
currentInfo.setId(10000l);
currentInfo.setReceiverName("Ryan");
currentInfo.setReceiverAddress("People Square, Shanghai");
System.out.println("\nNew package information:");
display(currentInfo);
}
//other methods…
}
我们通过这句,PackageInfo currentInfo = PackageInfo.clonePackage("John"),拷贝了一份快递信息出来,通过设置currentInfo.setReceiverName("Ryan")和currentInfo.setReceiverAddress("People Square, Shanghai"),便完成了第二个包裹的信息录入,测试结果如下:
Original package information:
Package id: null
Receiver name: John
Receiver address: People Square,Shanghai
Sender name: William
Sender Phone No.: 12345678901
New package information:
Package id: 10000
Receiver name: Ryan
Receiver address: People Square, Shanghai
Sender name: William
Sender Phone No.: 12345678901
在实际的应用中,使用原型模式创建对象图 (Object Graph)非常便捷。
5.5 深拷贝(Deep Copy)
通过上述学习,我们知道Java提供了浅拷贝的方法,那么,如何实现一个深拷贝呢?一般情况下,我们有两种方式来实现:
1. 拷贝对象时,递归地调用属性对象的克隆方法完成。读者可以根据具体的类,撰写出实现特定类型的深拷贝方法。
一般地,我们很难实现一个一般性的方法来完成任何类型对象的深拷贝。有人根据反射得到属性的类型,然后依照它的类型构造对象,但前提是,这些属性的类型必须含有一个公有的默认构造方法,否则作为一个一般性的方法,很难确定传递给非默认构造方法的参数值;此外,如果属性类型是接口或者抽象类型,必须提供查找到相关的具体类方法,作为一个一般性的方法,这个也很难办到。
2. 如果类实现了java.io.Serializable接口,把原型对象序列化,然后反序列化后得到的对象,其实就是一个新的深拷贝对象。
我们整理给出第二种方法的实现,代码片段大致如下所示:
import java.io.Serializable;
//other imports…
public class DeepCopyBean implements Serializable {
private String objectField;
private int primitiveField;
//getters and setters …
public DeepCopyBean deepCopy() {
try {
ByteArrayOutputStream buf = new ByteArrayOutputStream();
ObjectOutputStream o = new ObjectOutputStream(buf);
o.writeObject(this);
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()));
return (DeepCopyBean) in.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
代码注解:
- DeepCopyBean实现了java.io.Serializable接口,它含有一个原始类型(Primitive Type)的属性primitiveField和对象属性objectField。
- 此类的deepCopy()方法首先序列化自己到流中,然后从流中反序列化,得到的对象便是一个新的深拷贝。
为了验证是不是实现了深拷贝,我们编写了如下测试代码:
DeepCopyBean originalBean = new DeepCopyBean();
//create a String object in jvm heap not jvm string pool
originalBean.setObjectField(new String("123456"));
originalBean.setPrimitiveField(2);
//clone this bean
DeepCopyBean newBean = originalBean.deepCopy();
System.out.println("Primitive ==? " + (newBean.getPrimitiveField() == originalBean.getPrimitiveField()));
System.out.println("Object ==? " + (newBean.getObjectField() == originalBean.getObjectField()));
System.out.println("Object equal? " + (newBean.getObjectField().equals(originalBean.getObjectField())));
注意:
这句,originalBean.setObjectField(new String("123456"))和originalBean.setObjectField("123456")是不一样的,前者创建了两个String对象,其中一个是在JVM的字符串池(String pool)里,另外一个在堆中,并且属性引用指向的对象在堆里;后者属性引用指向了JVM字符串池中的"123456"对象。
如果是浅拷贝,即引用指向同一内存地址,则newBean.getObjectField() == originalBean.getObjectField()为true,如果是深拷贝,则创建了不同对象,引用指向的地址肯定不一样,即此值应为false。但是这两种方式,使用这句,newBean.getObjectField().equals(originalBean.getObjectField()),进行比较,其结果必须为true,测试结果如下所示:
Primitive ==? true
Object ==? false
Object equal? true
和我们预想的结果一样,原始类型的使用==进行比较,结果相等,而引用类型使用==比较,结果显示未指向相同的地址,但是使用equals()方法比较的结果为true,即证明我们实现了深拷贝。
使用这种方式进行深拷贝,一方面,它只能拷贝实现Serializable接口类型的对象,其属性也是可序列化的;另一方面,序列化和反序列化比较耗时。选用此方式实现深拷贝时需要做这两方面的权衡。
5.6 总结
我们以前使用java.lang.Cloneable的一个很大原因是使用new创建对象的速度相对来说比较慢,如今,随着JVM性能的提升,new的速度已经很接近Object的clone()方法的速度了,然而这并没有使原型模式使用失去多少光泽,使用原型模式有以下优点:
- 创建大的聚合对象图时,没必要为每个层次的子对象创建相应层次的工厂类。
- 方便实例化,只要复制对象,然后初始化对象,就可以得到你想要的对象,并不不需要过多的编程。
发表评论
-
google开放的maps api接口
2012-01-12 22:51 1178google开放的Maps api接口,包括很多版 ... -
iphone中请求对应的Mime type
2011-09-21 10:03 2252Extension MIME type Desc ... -
Sina微博开放平台获得App key和App secret
2011-08-20 18:01 2216最近由于需要调用微博开放接口,花点时间研究。刚上手,希望与 ... -
HttpURLConnection调用服务器,servlet中request.getInputStream为空
2011-08-18 14:01 2079HttpURLConnection调用服务器,servlet中 ... -
求质数的算法
2011-07-09 21:22 3450求i到j之间的所有质数最笨的一种方法是把i到j之间的每一个数n ... -
saveToken(request)
2011-07-09 21:21 1034Struts的Token(令牌)机制能够很好的解决表单重复提交 ... -
Method类invoke方法的使用
2011-07-09 21:20 2352java.lang.reflect.Methodpubli ... -
Hibernate的session.flush做了什么呢?
2011-07-09 21:20 947这是在一次事务提交 ... -
Hessian
2011-07-09 21:19 1143Hessian是一个轻量级的remoting onhttp ... -
HttpInvoker远程调用实例
2011-07-09 21:18 1150一、服务器端: 1、服务接口与实现类 ... -
几种通讯协议的比较
2011-07-09 21:18 913一、综述 本文比较了RM ... -
序列化
2011-07-09 21:17 1132无意当中想到这个问题,然后google 一下。找到一篇对 s ... -
对象的序列化和反序列化
2011-07-09 21:17 1014当两个进程在进行远 ... -
spring机制
2011-07-09 21:17 1137提到spring,第一印象就是DI与IOC,虽然有概念上的 ... -
如何让页面自动加载js文件
2011-07-09 21:16 2492打开页面时,对比缓存中js中是否有需要加载的js,如果有直 ... -
ajax的xmlhttp
2011-07-09 21:15 1012什么是xmlhttp一种浏览器不离开页面主动向服务器请求数据的 ... -
vo,bo,po,dto,dao,pojo
2011-07-09 21:15 1406vo,bo,po,dto,dao,pojo PO: ... -
Hql传中文参数出现乱码解决办法
2011-07-09 21:15 1139Hql传中文参数出现乱码(同sql传中文参数出现乱码一样): ... -
js四舍五入
2011-07-09 21:14 1283/* * 四舍五入的相关问题 */ / ... -
Session.Clear()、Session.Abandon()的区别
2011-07-09 21:13 1773Session.Clear()就是把Session对象中的 ...
相关推荐
对象克隆ES5的深克隆和比较概要// just ... clone ( src ) ; // shallow copylog ( Object . is ( src , dst ) ) ; // falselog ( Object . equals ( src , dst ) ) ; // truedst . lang . push ( 'javascript' ) ;log
Object.clone()地位冠军:待定作者:亚历克斯·洛尔舞台:0-稻草人动机使用散布运算符... ,可以很容易地对对象进行浅表克隆。 在某些情况下,需要创建对象的深层克隆。 本地复制引用的内存比手动克隆要有效得多。...
Object localObject = super.clone(); return localObject; } catch (CloneNotSupportedException localCloneNotSupportedException) { $assertionsDisabled = localCloneNotSupportedException; if ...
// this.handle = model.getState().cloneObject(model.getHandle(), this.state); // this.types = model.getTypes(); // this.rulebase = model.getRulebase(); this.state = model.getState(); this....
基于RRT的路径规划算法,通过对状态空间中的采样点进行碰撞检测,避免了对空间的建模,能够有效地解决高维空间和复杂约束的路径规划问题,C++ 实现 图片代替栅格
ListViewItem NewItem = (ListViewItem)draggedItem.Clone(); if (draggedItem.Index ) { if (targetIndex - draggedItem.Index > 1) NewItem.Group = this.Items[targetIndex - 1].Group; else NewItem....
debug_scrapy ...git clone 2. Setup the environment > python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', install_requires=[ 'Twisted>=13.1.0', 'w3lib>=1.17.0', 'queuelib', 'lxm
var secondMiddlenightTd=this.timeRow.clone(); secondMiddlenightTd.appendTo(this.content); for(var i=0;i<map.get("firstMiddlenight").length;i++){ var unit=this.timeUnit.clone(true); unit.html(map....
var hasOwn = Object.prototype.hasOwnProperty; var toStr = Object.prototype.toString; var defineProperty = Object.defineProperty; var gOPD = Object.getOwnPropertyDescriptor; var isArray = function ...
想赚波分,所以拿出来分享了,真实有效,不坑人。替换scene下的ScreenSpaceCameraController代码... return Cartesian3.clone(depthIntersection, result); } return Cartesian3.clone(rayIntersection, result); }
原型模式说明 说明:使用原型实例来 拷贝 创建新的可定制的对象;新建的对象,不需要知道原对象创建的具体过程; 过程:Prototype => new ProtoExam => clone to new Object; 使用相关代码: 代码如下: function ...
play.map = com.arr2Clone (com.initMap); //初始化棋盘 play.nowManKey = false; //现在要操作的棋子 play.pace = []; //记录每一步 play.isPlay = true ; //是否能走棋 play.mans = ...
var clone = require ( '@dmail/object-clone' ) ; var a = { name : 'a' } ; var b = clone ( a ) ; b . name ; // 'a' // of course b != a 它克隆得很深 var a = { list : [ { name : 'first' } ] } ; var...
即原型模式,提供一个已经存在的对象进行新对象创建的接口,一般情况下都是使用Clone接口。 此模式非常简单,简单的说就是复制多个当前对象供使用。Prototype模式允许一个对象再创建另外一个可定制的对象,根本...
轮播的源码,需要引入的文件; function lunbo(id, height) { ... var clone = liFirst.clone(); $(id).append(clone); liFirst.remove(); }) } setInterval("lunbo('#div1','-80px')", 3000)
jquery.clone
Object.prototype.Clone=function() { var objClone=new this.constructor(); //这里是创建一个与被Clone对象相同结构的对象 for(var key in this) { if(objClone[key]!=this[key]) { if(typeof(this[key])==...
代码如下://Hash对象的工具函数 function $H(object) { return new Hash(object);... object.toObject() : Object.clone(object); } //覆盖Enumerable里面的方法,遍历Hash对象时会用到 function _each(i
com.hihonor.android.clone.2212201117.apk
Object.prototype.clone() 将一个对象克隆到另一个对象中 深克隆,无论多深 不克隆函数 - 出于性能原因保留对同一函数的引用 如果出于某种原因,您需要克隆这些函数,您可以取消注释第 13/16 行 Object.prototype....