生活场景:招商信用卡给客户发送电子邮件
现在好多人都会使用信用卡,而信用卡中心会每月向用户发送账单或者其他广告或者活动。比如现在招商信用卡中心要向10个客户发送一条抽奖活动的邮件,这10个人接收到的邮件的内容肯定是一样的,不一样的地方就是客户的一些基本信息展示。因此,我们可以使用一个通用的模板,然后把客户的信息放到模板中生成一份完整的电子邮件。根据分析,我们设计如下类图:
代码清单如下。模板类:
邮件类:
场景类:
运行结果如下:
存在的问题
上面代码已经实现了向客户发送邮件的功能,但是我们没有考虑到效率问题,这只是演示向10个用户发送邮件,而现实业务中,信用卡中心的用户数量可能是百万或千万级别的,假设发送一份邮件的时间为0.02秒(包括操作数据库时间),那要向所有用户发送邮件的时间就是百万或千万个0.02秒,几天都发不完,所以,我们必须要把发送邮件的代码改为多线程的,已达到多个线程同时发送邮件来提升程序性能。
但这样又会出现线程安全的问题,比如线程1设置完邮件的信息之后,还没发出去,线程2启动了,把mail的收件人地址和称谓改掉了,结果应该发给张三的邮件发到李四的邮箱地址了,这样显然也是不行的。
这里提供一种解决方案:通过对象的复制功能来解决这个问题。
优化后的程序
我们首先修改类图如下:
我们让Mail类实现了一个Cloneable接口并重写clone()方法:
再修改下场景类响应的实现:
注意看Client类中的 try 块中,cloneMail对象的获取方式,是通过mail.clone()这个方法复制产生的一个新对象,和原有对象一样,然后我们只需修改其细节数据(此处是收件人称为和邮箱地址)即可。
原型模式的定义
上面这种不通过 new 关键字来产生一个对象,而是通过对象复制来实现的模式就叫做原型模式。
原型模式的核心是一个clone方法,通过该方法进行对象的拷贝,Java中提供了一个Cloneable接口来标示该对象是可拷贝的。
原型模式的通用代码如下:
原型模式的应用
原型模式具有性能优良的优点,它是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个大的循环体内产生大量的对象时,原型模式可以更好地体现这个优点。
注:原型模式的拷贝是从内存(堆内存)中以二进制流的方式进行拷贝,重新分配一个内存块,并不会执行任何构造函数。
了解的它的优点,我们就可以知道在哪些场景下可以使用原型模式了:
1. 资源优化场景
类初始化需要消耗非常多的资源,这个资源包括数据、硬件资源等。
2. 性能和安全要求的场景
通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
3. 一个对象多个修改者的场景(例子中的场景)
一个对象需要提供给其他对象访问,而且各个调用者可能都需要对其进行修改时,可以考虑使用原型模式拷贝多个对象供调用者使用。
扩展:浅拷贝和深拷贝
在了解深浅拷贝之前,我们先来看一个测试。
首先定义一个Thing类:
测试类:
猜想一下,运行结果应该是什么?实际运行结果如下:[张三, 李四]。
怎么会这样呢?为什么会有李四呢?因为Java做了一个偷懒的拷贝动作,Object类提供的clone()方法只是拷贝本对象,对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做浅拷贝。这种方式非常不安全,任何一个拷贝对象改变了私有变量的值,其他所有对象的变量的值都会随着改变,所以在实际项目中使用还是比较少的。
注:对象内部的数组和引用对象才不拷贝,其他的基本数据类型如int、byte、long等都会被拷贝,但对于String类型,Java就希望你把它认为是基本类型,它是没有clone方法的,我们在使用的时候就把String当做基本类型使用即可。
通过上面的分析我们知道,浅拷贝是有风险的,我们对上面的测试代码进行修改如下:
仅仅增加了红框中的内容,对私有的类变量进行独立的拷贝,Client类没有任何改变,运行结果如下:[张三]。
这样就实现了完全的拷贝,两个对象之间没有任何瓜葛,你改你的,我改我的,互不影响,这种拷贝就叫做深拷贝。
点个关注吧!进入我的主页查看更多干货!
举报/反馈

Java架构成长之路

230获赞 658粉丝
专注Java领域,分享Java进阶、架构技术。
关注
0
0
收藏
分享