场景
设计模式-原型模式-浅克隆和深克隆在Java中的使用示例:
https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/127576328
上面初步记录了原型模式的使用示例,下面再记录一个银行节假日或者搞活动
时发送邮件的例子。
原型模式
原型模式(Prototype Pattern)的简单程度仅次于单例模式和迭代器模式。正是由于简单,使用的场景才非常地多,
其定义如下:
Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.
(用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。)
简单,太简单了!原型模式的核心是一个clone方法,通过该方法进行对象的拷贝,Java提供了一个Cloneable接口来标示
这个对象是可拷贝的,为什么说是“标示”呢?翻开JDK的帮助看看Cloneable是一个方法都没有的,这个接口只是一个标记作用,
在JVM中具有这个标记的对象才有可能被拷贝。那怎么才能从“有可能被拷贝”转换为“可以被拷贝”呢?
方法是覆盖clone()方法,是的,你没有看错是重写clone()方法。
原型模式的优点
性能优良
原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,
原型模式可以更好地体现其优点。
逃避构造函数的约束
这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的**。优点就是减少了约束,
缺点也是减少了约束,需要大家在实际应用时考虑。
原型模式的使用场景
资源优化场景
类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
性能和安全要求的场景
通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
一个对象多个修改者的场景
一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,
然后由工厂方法提供给调用者。
原型模式的注意事项
构造函数不会被执行
一个实现了Cloneable并重写了clone方法的类A,有一个无参构造或有参构造B,通过new关键字产生了一个对象S,
再然后通过S.clone()方式产生了一个新的对象T,那么在对象拷贝时构造函数B是不会被执行的。具体可以参考示例代码。
浅拷贝和深拷贝
注意:深拷贝和浅拷贝建议不要混合使用,特别是在涉及类的继承时,父类有多个引用的情况就非常复杂,
建议的方案是深拷贝和浅拷贝分开实现。
关于浅拷贝和深拷贝可以参考示例代码。
clone与final两个冤家
对象的clone与对象内的final关键字是有冲突的,你要使用clone方法,在类的成员变量上就不要增加final关键字。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
实现
1、某银行节日发送抽奖活动通知邮件。
新建广告信模板代码
@Data public class AdvTemplate { //广告信名称 private String advSubject = "xx银行抽奖活动"; //广告信内容 private String advContext = "抽奖活动通知:......"; }
2、新建邮件类代码
//邮件类代码 @Data public class Mail { //收件人 private String receiver; //邮件名称 private String subject; //称谓 private String appellation; //内容 private String context; //邮件底部,一般是加上"xxx版权所有"等信息 private String tail; public Mail(AdvTemplate advTemplate){ this.context = advTemplate.getAdvContext(); this.subject = advTemplate.getAdvSubject(); } }
3、场景类
//场景类 public class Client { //发送账单的数量 private static int MAX_COUNT = 6; public static void main(String[] args) { //模拟发送邮件 int i =0; //把模板定义出来,这个一般从数据库中获取 Mail mail = new Mail(new AdvTemplate()); mail.setTail("badaoxxx版权所有"); while (i<MAX_COUNT){ //每封邮件不同的地方 mail.setAppellation(i+"先生/女士"); mail.setReceiver(i+"123@123.com"); sendMail(mail); i++; } } public static void sendMail(Mail mail){ System.out.println("标题:"+mail.getSubject()+"\t收件人:"+mail.getReceiver()+"\t...发送成功!"); } }
问题:
发送的单线程的,需要把sendMail修改为多线程,也会有问题,产生第一封邮件对象,放到线程一中运行,还没有发送出去;
线程2也启动了,直接就把邮件对象的的收件人和称谓给改了,线程不安全了,当然这有其它N多中解决方法,
其中一种就是使用一种新型模式来解决这个问题:通过对象的复制功能来解决这个问题。
4、修改后的邮件类
//修改后的邮件类 @Data public class MailExtend implements Cloneable{ //收件人 private String receiver; //邮件名称 private String subject; //称谓 private String appellation; //内容 private String context; //邮件底部,一般是加上"xxx版权所有"等信息 private String tail; public MailExtend(AdvTemplate advTemplate){ this.context = advTemplate.getAdvContext(); this.subject = advTemplate.getAdvSubject(); } @Override public MailExtend clone() throws CloneNotSupportedException { MailExtend mailExtend = null; mailExtend = (MailExtend) super.clone(); return mailExtend; } }
5、修改后的场景类
//修改后的场景类 public class ClientExtend { //发送账单的数量 private static int MAX_COUNT = 6; public static void main(String[] args) throws CloneNotSupportedException { //模拟发送邮件 int i =0; //把模板定义出来,这个一般从数据库中获取 MailExtend mail = new MailExtend(new AdvTemplate()); mail.setTail("badaoxxx版权所有"); while (i<MAX_COUNT){ //每封邮件不同的地方 MailExtend cloneMail = mail.clone(); mail.setAppellation(i+"先生/女士"); mail.setReceiver(i+"123@123.com"); sendMail(cloneMail); i++; } } public static void sendMail(MailExtend mail){ System.out.println("标题:"+mail.getSubject()+"\t收件人:"+mail.getReceiver()+"\t...发送成功!"); } }
运行结果不变,而且sendMail即使是多线程也没有关系。
Client类中的mail.clone(),把对象复制一份,产生一个新的对象,和原有对象一样,然后再修改细节的数据,
如设置称谓、收件人地址等。这种不通过new 关键字来产生一个对象,而是通过对象复制来实现的模式就叫做原型模式。
6、验证clone之后的构造函数不会被执行
//构造函数不会被执行 public class Thing implements Cloneable{ public Thing(){ System.out.println("构造函数被执行了..."); } @Override public Thing clone() throws CloneNotSupportedException { Thing thing = null; thing = (Thing)super.clone(); return thing; } }
再写一个Client 进行对象的拷贝
public class TestClient { public static void main(String[] args) throws CloneNotSupportedException { Thing thing = new Thing(); Thing cloneThing = thing.clone(); } }
从运行结果中看到,对象拷贝时构造函数没有被执行
Object类的clone方法的原理是从内存中以二进制流的方式进行拷贝,重新分配一个内存块。
7、浅克隆与深克隆
看一个浅克隆的例子
public class QianClone implements Cloneable{ //定义一个私有变量 private ArrayList<String> arrayList = new ArrayList<>(); @Override public QianClone clone() throws CloneNotSupportedException { QianClone thing = null; thing = (QianClone)super.clone(); return thing; } public void setValue(String value){ this.arrayList.add(value); } public ArrayList<String> getValue(){ return this.arrayList; } }
调用浅克隆的场景类
public class QianCloneClient { public static void main(String[] args) throws CloneNotSupportedException { QianClone qianClone = new QianClone(); qianClone.setValue("张三"); QianClone qianCloneClone = qianClone.clone(); qianCloneClone.setValue("李四"); System.out.println(qianClone.getValue()); //[张三, 李四] } }
增加一个私有变量arrayList,然后运行结果不光有张三,还有李四。
是因为Object类的clone方法只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,
这种拷贝就是浅拷贝。两个对象共享了一个私有变量,你改大家都能改。那么为什么在Mail那个类中就可以使用String类型,
而不会产生由浅拷贝带来的问题。因为内部的数组和对象才不拷贝,其它的原始类型int、long、cha等以及String都会被拷贝。
深拷贝的例子
//深拷贝 public class ShenClone implements Cloneable{ //定义一个私有变量 private ArrayList<String> arrayList = new ArrayList<>(); @Override public ShenClone clone() throws CloneNotSupportedException { ShenClone thing = null; thing = (ShenClone)super.clone(); thing.arrayList = (ArrayList<String>) this.arrayList.clone(); return thing; } public void setValue(String value){ this.arrayList.add(value); } public ArrayList<String> getValue(){ return this.arrayList; } }
深拷贝场景类
public class ShenCloneClient { public static void main(String[] args) throws CloneNotSupportedException { ShenClone shenClone = new ShenClone(); shenClone.setValue("张三"); ShenClone shenClone1 = shenClone.clone(); shenClone1.setValue("李四"); System.out.println(shenClone.getValue()); //[张三] } }
对私有的类变量进行独立的拷贝。
该方法实现了完全的拷贝,两个对象之间没有任何的瓜葛了,不互相影响,这种就是深拷贝。
标签:Java,String,示例,对象,clone,private,拷贝,设计模式,public From: https://www.cnblogs.com/badaoliumangqizhi/p/17348673.html