原型模式(Prototype):用原型实例指定创建对象的 种类,并且通过拷贝这些原型创建新的对象。
原型模式结构图:
原型模式涉及深克隆和浅克隆。
案例需求:制作一份简历,并复制三份。
第一次克隆实现:
1:创建简历类。
package PrototypeModel; /** * 简历类 * @author 我不是张英俊 * */ public class Resume implements Cloneable { private String name; private String sex; private String age; private String timeArea; private String company; public Resume(){ } public Resume(String name){ this.name=name; } //设置个人信息 public void SetPersonalInfo(String sex,String age){ this.sex=sex; this.age=age; } //设置工作经历 public void SetWorkExperience(String timeArea,String company){ this.timeArea=timeArea; this.company=company; } //显示 public void Display(){ System.out.println(name+" "+sex+" "+age); System.out.println("工作经历:"+timeArea+" "+company); } public Object Copy() throws CloneNotSupportedException { return this; } public Object clone() throws CloneNotSupportedException{ //调用父类Object中的clone方法实现浅克隆 return super.clone(); } }
2:创建测试类
package PrototypeModel; /** * 简历复印,打印一份简历,并复印三份; * 原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 * @author 我不是张英俊 * */ public class Test { public static void main(String[] args) throws CloneNotSupportedException { Resume a=new Resume("旺财"); a.SetPersonalInfo("男", "19"); a.SetWorkExperience("1998-2-3", "百度"); /** * Cpoy 方法中的是return this, * 返回的是索引,即返回的是指向堆中a对象的索引, * 所以,不论怎么重新改变属性,最终改变的都是a对象中的属性, * 而此时,并没有在堆中重新copy出两个新的对象b和c。 * 所以,应该用clone方法来实行, * 继承Cloneable接口,并且重写Object中的clone方法。 * Cloneable接口是一个空的接口,它只是表明,可以重写clone方法,若不实现此接口,不可以重写clone方法 * 此处使用的是浅克隆。 */ Resume b= (Resume) a.Copy(); Resume b1=(Resume) a.clone(); b.SetWorkExperience("2019-02-06", "360"); Resume c=(Resume) a.Copy(); Resume c1=(Resume) a.clone(); a.SetWorkExperience("2016-03-02", "阿里"); System.out.println("a= "+a); System.out.println("b= "+b); System.out.println("c= "+c); System.out.println("b1= "+b1); System.out.println("c1= "+c1); /** * 控制台输出发现克隆的索引不同,即创建出了新的对象。 */ a.Display(); b.Display(); c.Display(); a.Display(); b1.Display(); c1.Display(); } }
3:控制台
a= PrototypeModel.Resume@6a5c2445 b= PrototypeModel.Resume@6a5c2445 c= PrototypeModel.Resume@6a5c2445 b1= PrototypeModel.Resume@47516490 c1= PrototypeModel.Resume@30a14e84 旺财 男 19 工作经历:2016-03-02 阿里 旺财 男 19 工作经历:2016-03-02 阿里 旺财 男 19 工作经历:2016-03-02 阿里 旺财 男 19 工作经历:2016-03-02 阿里 旺财 男 19 工作经历:1998-2-3 百度 旺财 男 19 工作经历:2019-02-06 360
a,b,c三个的地址相同,说明return this返回当前对象的时候返回的是索引,他们指向同一个对象。
而b1,c1地址不同,说明是利用clone方法创建了新的对象。
super.clone()返回的是Object类型的。利用Object类中的clone方法来进行浅克隆。重写Object类中的clone方法需要实现Cloneable接口。
其中Cloneable接口是一个空接口,它只是表明你可以重写clone方法,不实现重写的话,会抛出异常。
第二次克隆实现
事实上在实际开发过程中,工作经历通常会做成工作经历类,这样,就需要深克隆。
先看如果使用浅克隆导致的问题:
1:创建工作经历类。
package PrototypeModel2; public class workExperence { private String workDate; private String company; public String getWorkDate() { return workDate; } public void setWorkDate(String workDate) { this.workDate = workDate; } public String getCompany() { return company; } public void setCompany(String company) { this.company = company; } }
2:创建简历类。
package PrototypeModel2; public class Resume implements Cloneable { private String name; private String sex; private String age; private workExperence work; public Resume(String name){ this.name=name; work=new workExperence(); } //设置个人信息 public void setPersonalInfo(String sex,String age){ this.sex=sex; this.age=age; } //设置工作经历 public void setWorkExperience(String workDate,String company){ work.setCompany(company); work.setWorkDate(workDate); } //显示 public void display(){ System.out.println(name+" "+sex+" "+age); System.out.println(work.getCompany()+" "+work.getWorkDate()); } public Object clone() throws CloneNotSupportedException{ return super.clone(); } }
3:测试类。
package PrototypeModel2; /** * 浅克隆会导致的问题 * 制作简历 * 实际开发中,会将工作经历作为一个类, * @author 我不是张英俊 * */ public class Test { public static void main(String arg[]) throws CloneNotSupportedException{ Resume a=new Resume("旺财"); a.setPersonalInfo("男", "26"); a.setWorkExperience("2016-1017", "阿里"); Resume b=(Resume) a.clone(); b.setWorkExperience("2017-2018", "360"); Resume c=(Resume) a.clone(); a.setWorkExperience("2018-2019", "腾讯"); a.display(); b.display(); c.display(); } }
4:控制台。
旺财 男 26 腾讯 2018-2019 旺财 男 26 腾讯 2018-2019 旺财 男 26 腾讯 2018-2019
原因:堆内存中只存在一个workExperience类,所以在修改的时候,总是修改同一个类中的数据,虽然克隆了不同的Resume对象,但不同的Resume对象确是调用同一个workExperience中的数据,所以最后输出的都是最后修改的。
因为clone()方法,对于引用类型,克隆的是其引用,所以,当改变值得时候,就会出现相同的结果。因为三个引用都指向了同一个对象,即唯一的workExperience对象。
如果想显示不同的,就必须对workExperience进行克隆,这样每一个引用不同的workExperience就不会出现这种问题。
第三次克隆实现:
1:创建工作经历类
package PrototypeModel1; /** * 工作经历类 * @author 我不是张英俊 * */ public class WorkExperience implements Cloneable { private String workDate; private String company; public String getWorkDate() { return workDate; } public void setWorkDate(String workDate) { this.workDate = workDate; } public String getCompany() { return company; } public void setCompany(String company) { this.company = company; } public Object clone() throws CloneNotSupportedException{ return super.clone(); } }
2:创建一个简历类。
package PrototypeModel1; public class Resume implements Cloneable { private String name ; private String sex; private String age; private WorkExperience work; public Resume(String name){ this.name=name; work=new WorkExperience(); } /* *提供clone方法调用的私有构造函数,以便克隆“工作经历”的数据 */ private Resume(WorkExperience work) throws CloneNotSupportedException{ this.work=(WorkExperience) work.clone(); } //设置个人信息 public void setPersonalInfo(String sex,String age){ this.sex=sex; this.age=age; } //设置工作经历 public void setWorkExperience(String workDate,String company){ work.setCompany(company); work.setWorkDate(workDate); } //展示 public void display(){ System.out.println(name+" "+sex+" "+age); System.out.println("工作经历 "+work.getCompany()+" "+work.getWorkDate()); } public Object clone() throws CloneNotSupportedException{ /** * 调用私有的构造方法,让“工作经历”克隆完成,然后再给这个“简历” * 对象相关字段赋值,最终返回一个深克隆的简历对象。 */ Resume obj=new Resume(this.work); obj.age=this.age; obj.name=this.name; obj.sex=this.sex; return obj; } }
3:创建一个测试类。
package PrototypeModel1; /** * 简历复印, * 原型模式, * 深拷贝和浅拷贝 * @author 我不是张英俊 * */ public class Test { public static void main(String[] args) throws CloneNotSupportedException { Resume a =new Resume("旺财"); a.setPersonalInfo("男", "19"); a.setWorkExperience("2019-2020", "阿里"); Resume b=(Resume)a.clone(); b.setWorkExperience("2017-2018", "腾讯"); Resume c=(Resume)a.clone(); c.setWorkExperience("2016-2017", "华为"); a.display(); b.display(); c.display(); } }
4:控制台。
旺财 男 19
工作经历 阿里 2019-2020
旺财 男 19
工作经历 腾讯 2017-2018
旺财 男 19
工作经历 华为 2016-2017
利用深克隆解决问题。创建了堆内存中实际存在了三个workExperience对象。
总结:理解尚浅,暂时未想到。