原型模式
原型模式的适用场景:
1.类初始化消耗资源较多
2.new一个对象需要非常繁琐的过程(数据准备,访问权限等)
3.构造函数比较复杂
4.循环产生大量对象时
优点:
1.原型模式比new一个对象性能要高
2.简化创建过程
缺点:
1.必须配备克隆方法(必须重写object的克隆方法)
2.对克隆对象,进行复杂改造的时候,容易引入风险
重要的是克隆
常用的场景:积分抽奖,邮箱,短信
浅克隆:
Cloneable接口 重写clone方法
public class Email implements Cloneable{
private String name;
private String address;
private String content;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "Email{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
", content='" + content + '\'' +
'}'+super.toString();
}
}
发送邮件,保存邮件内容
public class EmailUtils {
public static void sendEmail(Email email){
String sendfriend="向{0}发送,邮件地址:{1},邮件内容:{2}";
System.out.println(MessageFormat.format(sendfriend, email.getName(),email.getAddress(),email.getContent()));
}
public static void saveOriginMailRecord(Email email){
System.out.println("存储原始email:"+email.getContent());
}
}
Email clone=(Email)email.clone(); 克隆
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Email email=new Email();
email.setContent("测试模板");
System.out.println(email);
for (int i = 0; i < 10; i++) {
Email clone=(Email)email.clone();
clone.setName("姓名"+i);
clone.setAddress("qq号"+i+"@qq.com");
clone.setContent("恭喜您中奖qq币");
EmailUtils.sendEmail(clone);
System.out.println("CLONE"+clone);
}
EmailUtils.saveOriginMailRecord(email);
}
}
输出结果
Email{name='null', address='null', content='测试模板'}com.qjc.pattern.creational.prototype.Email@4554617c
向姓名0发送,邮件地址:qq号0@qq.com,邮件内容:恭喜您中奖qq币
CLONEEmail{name='姓名0', address='qq号0@qq.com', content='恭喜您中奖qq币'}com.qjc.pattern.creational.prototype.Email@74a14482
向姓名1发送,邮件地址:qq号1@qq.com,邮件内容:恭喜您中奖qq币
CLONEEmail{name='姓名1', address='qq号1@qq.com', content='恭喜您中奖qq币'}com.qjc.pattern.creational.prototype.Email@1540e19d
向姓名2发送,邮件地址:qq号2@qq.com,邮件内容:恭喜您中奖qq币
CLONEEmail{name='姓名2', address='qq号2@qq.com', content='恭喜您中奖qq币'}com.qjc.pattern.creational.prototype.Email@677327b6
向姓名3发送,邮件地址:qq号3@qq.com,邮件内容:恭喜您中奖qq币
CLONEEmail{name='姓名3', address='qq号3@qq.com', content='恭喜您中奖qq币'}com.qjc.pattern.creational.prototype.Email@14ae5a5
向姓名4发送,邮件地址:qq号4@qq.com,邮件内容:恭喜您中奖qq币
CLONEEmail{name='姓名4', address='qq号4@qq.com', content='恭喜您中奖qq币'}com.qjc.pattern.creational.prototype.Email@7f31245a
向姓名5发送,邮件地址:qq号5@qq.com,邮件内容:恭喜您中奖qq币
CLONEEmail{name='姓名5', address='qq号5@qq.com', content='恭喜您中奖qq币'}com.qjc.pattern.creational.prototype.Email@6d6f6e28
向姓名6发送,邮件地址:qq号6@qq.com,邮件内容:恭喜您中奖qq币
CLONEEmail{name='姓名6', address='qq号6@qq.com', content='恭喜您中奖qq币'}com.qjc.pattern.creational.prototype.Email@135fbaa4
向姓名7发送,邮件地址:qq号7@qq.com,邮件内容:恭喜您中奖qq币
CLONEEmail{name='姓名7', address='qq号7@qq.com', content='恭喜您中奖qq币'}com.qjc.pattern.creational.prototype.Email@45ee12a7
向姓名8发送,邮件地址:qq号8@qq.com,邮件内容:恭喜您中奖qq币
CLONEEmail{name='姓名8', address='qq号8@qq.com', content='恭喜您中奖qq币'}com.qjc.pattern.creational.prototype.Email@330bedb4
向姓名9发送,邮件地址:qq号9@qq.com,邮件内容:恭喜您中奖qq币
CLONEEmail{name='姓名9', address='qq号9@qq.com', content='恭喜您中奖qq币'}com.qjc.pattern.creational.prototype.Email@2503dbd3
存储原始email:测试模板
由此看出new对象一次,然后使用clone方法频繁复制对象
深克隆:
深克隆,就是对象里面包含了一个date对象如果在引用时
public class Pig implements Cloneable{
private String name;
private Date birthday;
public Pig(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
protected Object clone() throws CloneNotSupportedException {
//深克隆 很容易引起bug
return super.clone();
}
@Override
public String toString() {
return "Pig{" +
"name='" + name + '\'' +
", birthday=" + birthday +
'}'+super.toString();
}
}
//深克隆 浅克隆
Date date=new Date(0L);
Pig pig=new Pig("佩奇",date);
Pig pig1= (Pig) pig.clone();
System.out.println(pig);//date 引用的同一个对象
System.out.println(pig1);//date
pig.getBirthday().setTime(8888888888L);
//相同对象
System.out.println(pig);
System.out.println(pig1);
输出结果:
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.qjc.pattern.creational.prototype.clone.Pig@14ae5a5
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.qjc.pattern.creational.prototype.clone.Pig@7f31245a
Pig{name='佩奇', birthday=Tue Apr 14 05:08:08 CST 1970}com.qjc.pattern.creational.prototype.clone.Pig@14ae5a5
Pig{name='佩奇', birthday=Tue Apr 14 05:08:08 CST 1970}com.qjc.pattern.creational.prototype.clone.Pig@7f31245a
由此看出如果我修改了date,导致克隆对象也会被修改,这就是深克隆的问题
如何解决此问题,重写clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
Pig pig=(Pig)super.clone();
pig.birthday= (Date) pig.birthday.clone();
return pig;
}
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.qjc.pattern.creational.prototype.clone.Pig@14ae5a5
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.qjc.pattern.creational.prototype.clone.Pig@7f31245a
Pig{name='佩奇', birthday=Tue Apr 14 05:08:08 CST 1970}com.qjc.pattern.creational.prototype.clone.Pig@14ae5a5
Pig{name='佩奇', birthday=Thu Jan 01 08:00:00 CST 1970}com.qjc.pattern.creational.prototype.clone.Pig@7f31245a
这样的话date对象就不存在深克隆这个问题了
jdk中有:
HashMap,ArrayList 等实现了Cloneable接口有兴趣的参考一下
克隆破坏单例模式:懒汉式 这样的话就会出现两个对象
HungrySingleton hungrySingleton = HungrySingleton.getInstance();
Method method = hungrySingleton.getClass().getDeclaredMethod("clone");
method.setAccessible(true);
HungrySingleton cloneHungrySingleton = (HungrySingleton) method.invoke(hungrySingleton);
System.out.println(hungrySingleton);
System.out.println(cloneHungrySingleton);
如何防止这种方式,不实现Cloneable接口,或者让Cloneable,调用另一个方法getInstatance,即可解决