前言23设计模式速记单例模式工厂方法模式抽象工厂模式Builder/Builder模式23设计模式速记请看上面第一篇。在本文中,我们将与您一起学习原型模式。在学习原型模式之前,我们需要了解一下浅拷贝和深拷贝这两个概念。浅拷贝和深拷贝浅拷贝是对象的按位拷贝,它创建一个新对象,该对象具有原始对象属性值的精确副本。如果属性是基本类型,则复制基本类型的值;如果属性是内存地址(引用类型),内存地址被复制,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。对象的引用地址被复制,两个对象指向同一个内存地址,所以修改其中的任何一个值都会相应地改变另一个值。深拷贝深拷贝复制所有属性,并复制属性指向的动态分配的内存。当一个对象与其引用的对象一起被复制时,就会发生深拷贝。深拷贝比浅拷贝更慢且更昂贵。复制对象及其值,修改两个对象的任意一个值,另一个值不变。模式定义使用原型实例来指定要创建的对象的种类,并通过复制这些原型来创建新对象。原型模式实际上是一个对象创建另一个可定制的对象,不需要指定创建的任何细节。Java提供了Coneable接口,它有一个独特的方法Clone(),实现这个接口就可以完成原型模式。实例说明浅拷贝案例声明User实体类需要实现Cloneable接口并重写clone()方法。用户属性包括基本数据类型和引用数据类型,方便演示packagecom.niuh.designpattern.prototype;/***用户信息*/publicclassUserimplementsCloneable{//基本数据类型privateintid;privateStringname;privateStringsex;privateStringpwd;//引用数据类型privateBaseInfobaseInfo;publicUser(intid,Stringname,Stringsex,Stringpwd,BaseInfobaseInfo){this.id=id;this.name=name;this.sex=sex;this.pwd=pwd;this.baseInfo=baseInfo;}publicintgetId(){returnid;}publicvoidsetId(intid){this.id=id;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicStringgetSex(){returnsex;}publicvoidsetSex(Stringsex){this.sex=sex;}publicStringgetPwd(){returnpwd;}publicvoidsetPwd(Stringpwd){this.pwd=pwd;}publicBaseInfogetBaseInfo(){returnbaseInfo;}publicvoidsetBaseInfo(BaseInfobaseInfo){this.baseInfo=baseInfo;}@OverridepublicStringtoString(){return"+super.hashCode()+",User{"+"id="+id+",name='"+name+'\''+",sex='"+sex+'\''+",pwd='"+密码+'\''+",baseInfo="+baseInfo+'}';}@OverrideprotectedUserclone()throwsCloneNotSupportedException{return(User)super.clone();}}packagecom.niuh.designpattern.prototype;importjava.util.Date;/***基本类*/publicclassBaseInfo{privateStringdesc;//.....publicBaseInfo(Stringdesc){this.desc=desc;}publicStringgetDesc(){returndesc;}publicvoidsetDesc(Stringdesc){this.desc=desc;}@OverridepublicStringtoString(){return"BaseInfo{"+"desc="+desc+'}';}}packagecom.niuh.designpattern.prototype;/***原型设计模式*/publicclassPrototypePattern{publicstaticvoidmain(String[]args)throwsCloneNotSupportedException{BaseInfobaseInfo=newBaseInfo("张三");Useruser1=newUser(1,"张三","男","123456",baseInfo);//newUser……//克隆机制Useruser2=user1.clone();user2.setId(2);user2.setName("丽丝");BaseInfobaseInfo1=user2.getBaseInfo();baseInfo1.setDesc("丽丝");System.out.println(user1);System.out.println(user2);}}输出结果如下:从输出结果可以看出,thr如果你ser1.clone()复制对象后得到的user2是和user1不同的两个对象。HashCode值不同。user1和user2的基本数据类型的修改互不影响,但是引用类型baseInfo的修改会有影响。深拷贝案例从上面的例子可以看出,浅拷贝会给数据安全带来隐患。比如我们只想修改user2的baseInfo,但是user1的baseInfo也被修改了,因为他们都指向同一个地址。所以,在这种情况下,我们需要使用深拷贝。深拷贝,在拷贝引用类型成员变量时,为引用类型数据成员设置独立的内存空间,实现真正内容的拷贝。对于User引用类型的成员变量BaseInfo,需要实现Cloneable,重写clone()方法。packagecom.niuh.designpattern.prototype;importjava.util.Date;/***基础类*/publicclassBaseInfoimplementsCloneable{privateStringdesc;//.......publicBaseInfo(Stringdesc){this.desc=desc;}publicStringgetDesc(){returndesc;}publicvoidsetDesc(Stringdesc){this.desc=desc;}@OverridepublicStringtoString(){return"BaseInfo{"+"desc="+desc+'}';}@OverrideprotectedBaseInfoclone()throwsCloneNotSupportedException{//BaseInfo如果有同样是引用类型的成员属性,也应该在User的clone()方法中实现return(BaseInfo)super.clone();}},需要自己获取复制后生成的新对象,然后创建一个newobject为新对象引用类型,然后调用复制操作,实现引用类型成员变量的深拷贝。packagecom.niuh.designpattern.prototype;/***用户信息*/publicclassUserimplementsCloneable{//基础数据类型privateintid;privateStringname;privateStringsex;privateStringpwd;//引用数据类型privateBaseInfobaseInfo;publicUser(intid,StringinfobaseStringname,Strings){this.id=id;this.name=name;this.sex=sex;this.pwd=pwd;this.baseInfo=baseInfo;}publicintgetId(){returnid;}publicvoidsetId(intid){this.id=id;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicStringgetSex(){returnsex;}publicvoidsetSex(Stringsex){this.sex=sex;}publicStringgetPwd(){returnpwd;}publicvoidsetPwd(Stringpwd){this.pwd=pwd;}publicBaseInfogetBaseInfo(){returnbaseInfo;}publicvoidsetBaseInfo(BaseInfobaseInfo){this.baseInfo=baseInfo;}@OverridepublicStringtoString(){return"hashCode:"+super.hashCode()+",User{"+"id="+id+",name='"+name+'\''+",sex='"+sex+'\''+",pwd='"+pwd+'\''+",baseInfo="+baseInfo+'}';}@OverrideprotectedUserclone()throwsCloneNotSupportedException{//深拷贝Useruser=(User)super.clone();user.baseInfo=baseInfo.clone();returnuser;}}同上用法,输出结果如下:从输出结果可以看出,深拷贝后,无论是基本数据类型还是引用类型的成员变量,修改其值都不会相互影响。还需要实现Serializable接口。packagecom.niuh.designpattern.prototype;importjava.io.ByteArrayInputStream;importjava.io.ByteArrayOutputStream;importjava.io.IOException;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;importjava.io.Serializable;/***用户信息*/publicclassUserimplementsCloneable,Serializable{//基础数据类型privateintid;privateStringname;privateStringsex;privateStringpwd;//引用数据类型privateBaseInfobaseInfo;publicUser(intid,Stringname,Stringsex,Stringpwd,BaseInfobaseInfo){this.name=id;this.id=name;;this.sex=sex;this.pwd=pwd;this.baseInfo=baseInfo;}publicintgetId(){returnid;}publicvoidsetId(intid){this.id=id;}publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}publicStringgetSex(){returnsex;}publicvoidsetSex(Stringsex){this.sex=sex;}publicStringgetPwd(){returnpwd;}publicvoidsetPwd(Stringpwd){this.pwd=pwd;}publicBaseInfogetBaseInfo(){returnbaseInfo;}publicvoidsetBaseInfo(BaseInfobaseInfo){this.baseInfo=baseInfo;}@OverridepublicStringtoString(){return"hashCode:"+super.hashCode()+",User{"+"id="+id+",name='"+name+'\''+",sex='"+sex+'\''+",pwd='"+pwd+'\''+",baseInfo="+baseInfo+'}';}@OverrideprotectedUserclone()throwsCloneNotSupportedException{//深拷贝//Useruser=(User)super.clone();//user.baseInfo=baseInfo.clone();//returnuser;ByteArrayOutputStreambyteArrayOutputStream=newByteArrayOutputStream();try(ObjectOutputStreamoos=newObjectOutputStream(byteArrayOutputStream)){oos.writeObject(this);}catch(TraIOExceptione){e.printStack();}ByteArrayInputStreambyteArrayInputStream=newByteArrayInputStream(byteArrayOutputStream.toByteArray());尝试(ObjectInputStreamois=newObjectInputStream(byteArrayInputStream)){try{Useruser=(User)ois.readObject();returnuser;}catch(ClassNotFound)Catch(ClassNotFoundExcepce{);}}catch(IOExceptione){e.printStackTrace();}returnnull;}}此时没有使用Java深拷贝,改变成员属性Baseinfo也可以保持对象的独立性不建议使用序列化机制来完成深拷贝,因为序列化操作是CPU密集型操作,解析流很耗性能,速度会比较慢。好处是在不耦合具体类的情况下克隆对象更方便,避免重复初始化代码。构造复杂对象的缺点不是很广泛适用。每个类都必须配备一个克隆方法。配备clone方法需要综合考虑类的功能。这对于全新的类来说并不难,但是对于已有的类就不一定容易了,尤其是当一个类引用了不支持序列化的间接对象,或者引用中包含循环的时候。当结构。应用场景当代码不应依赖于需要复制的对象的具体类时,使用原型模式。创建一些具有复杂结构的对象;由于需求的变化,这些对象经常面临剧烈的变化,但它们有相对稳定和一致的接口;一般来说,在初始化信息不改变的情况下,克隆是最好的方法。源码中的应用#Springorg.springframework.beans.factory.support.AbstractBeanDefinition#JDKjava.util.Arraysjava.util.ArrayList......ArrayList中的ArrayList还有一个clone()方法,返回一个Object对象如下,所以在使用这个方法的时候,一定要进行转换。ArrayList的本质是维护一个Object数组,所以克隆也是通过复制数组来实现的,属于浅拷贝。@OverridepublicObjectclone(){try{ArrayList>result=(ArrayList>)super.clone();result.array=array.clone();returnresult;}catch(CloneNotSupportedExceptione){thrownewAssertionError();}}ArrayListClone浅拷贝的巧妙使用当你需要使用remove方法移除集合中的对象而不是修改集合中的对象时,你可以选择使用它。//添加两个元素StudentsstJack=newStudent("Jack",13);StudentstTom=newStudent("Tom",15);list.add(stJack);list.add(stTom);//CloneArrayList
