原型模式代码(原来可以这么简单)
原型模式代码(原来可以这么简单)public class Mail implements Cloneable{ private String name; private String emailAddress; private String content; public Mail() { System.out.println("Mail Class Constructor"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmailAddress() { return e
更多java文章与项目资源、毕业设计
关注公众号 程序猿forever
原型模式- 9-1 原型模式讲解
- 9-2 原型模式coding
- 9-3 原型模式coding-克隆破坏单例
- 9-4 原型模式源码解析
9-2 原型模式coding
有一个Mail类:
public class Mail {
private String name;
private String emailAddress;
private String content;
public Mail() {
System.out.println("Mail Class Constructor");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "Mail{"
"name='" name '\''
" emailAddress='" emailAddress '\''
" content='" content '\''
'}';
}
}
123456789101112131415161718192021222324252627282930313233343536373839404142
还有一个发送邮件的类:
注:这里面的MessageFormat.format方法是用来拼接字符串的;
public class MailUtil {
public static void sendMail(Mail mail) {
String outputContent = "向{0}同学,邮件地址:{1} 邮件内容:{2}发送邮件成功!";
System.out.println(MessageFormat.format(outputContent mail.getName() mail.getEmailAddress() mail.getContent()));
}
public static void saveOriginMailRecord(Mail mail) {
System.out.println("存储OriginMail记录 OriginMail:" mail.getContent());
}
}
12345678910
测试:
public class Test {
public static void main(String[]args){
Mail mail = new Mail();
mail.setContent("初始化的模板");
for (int i = 0; i < 10; i ) {
mail.setName("姓名" i);
mail.setEmailAddress("姓名" i "@126.com");
mail.setContent("恭喜您,此次活动中奖了!");
MailUtil.sendMail(mail);
}
MailUtil.saveOriginMailRecord(mail);
}
}
12345678910111213
测试结果:
Mail Class Constructor
向姓名0同学,邮件地址:姓名0@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
向姓名1同学,邮件地址:姓名1@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
向姓名2同学,邮件地址:姓名2@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
向姓名3同学,邮件地址:姓名3@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
向姓名4同学,邮件地址:姓名4@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
向姓名5同学,邮件地址:姓名5@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
向姓名6同学,邮件地址:姓名6@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
向姓名7同学,邮件地址:姓名7@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
向姓名8同学,邮件地址:姓名8@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
向姓名9同学,邮件地址:姓名9@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
存储OriginMail记录 OriginMail:恭喜您,此次活动中奖了!
现在,我们来用原型模式来写:
我们让Mail拥有克隆的功能,实现Cloneable接口:
public class Mail implements Cloneable{
private String name;
private String emailAddress;
private String content;
public Mail() {
System.out.println("Mail Class Constructor");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toString() {
return "Mail{"
"name='" name '\''
" emailAddress='" emailAddress '\''
" content='" content '\''
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {
System.out.println("Clone mail object");
return super.clone();
}
}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
测试:
public class Test {
public static void main(String[]args) throws CloneNotSupportedException {
Mail mail = new Mail();
mail.setContent("初始化的模板");
for (int i = 0; i < 10; i ) {
/** 使用克隆出来的Mail */
Mail mailTemp = (Mail) mail.clone();
mailTemp.setName("姓名" i);
mailTemp.setEmailAddress("姓名" i "@126.com");
mailTemp.setContent("恭喜您,此次活动中奖了!");
MailUtil.sendMail(mailTemp);
}
MailUtil.saveOriginMailRecord(mail);
}
}
123456789101112131415
测试结果:
Mail Class Constructor
Clone mail object
向姓名0同学,邮件地址:姓名0@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
Clone mail object
向姓名1同学,邮件地址:姓名1@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
Clone mail object
向姓名2同学,邮件地址:姓名2@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
Clone mail object
向姓名3同学,邮件地址:姓名3@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
Clone mail object
向姓名4同学,邮件地址:姓名4@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
Clone mail object
向姓名5同学,邮件地址:姓名5@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
Clone mail object
向姓名6同学,邮件地址:姓名6@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
Clone mail object
向姓名7同学,邮件地址:姓名7@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
Clone mail object
向姓名8同学,邮件地址:姓名8@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
Clone mail object
向姓名9同学,邮件地址:姓名9@126.com 邮件内容:恭喜您,此次活动中奖了!发送邮件成功!
Disconnected from the target VM address: ‘127.0.0.1:61704’ transport: ‘socket’
存储OriginMail记录 OriginMail:初始化的模板
在原型模式中,还有一种常见的使用方法:通过抽象类来实现原型模式
父类实现了克隆的接口:
public abstract class A implements Cloneable {
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
123456
子类也具有克隆的方法:
public class B extends A {
public static void main(String[]args) throws CloneNotSupportedException {
B b = new B();
b.clone();
}
}
123456
在实际的开发当中,直接让目标类实现Cloneable接口的方式用的比较多;
我们来说说克隆:深拷贝和浅拷贝
有一个Pig类:
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 {
return super.clone();
}
@Override
public String toString() {
return "Pig{"
"name='" name '\''
" birthday=" birthday
'}' super.toString();
}
}
1234567891011121314151617181920212223242526272829303132333435363738
测试:
public class Test {
public static void main(String[]args) throws CloneNotSupportedException {
Date birthday = new Date(0L);
Pig pig1 = new Pig("佩奇" birthday);
Pig pig2 = (Pig) pig1.clone();
System.out.println(pig1);
System.out.println(pig2);
}
}
123456789
测试结果:这是两个不同的对象
Pig{name=‘佩奇’ birthday=Thu Jan 01 08:00:00 CST 1970}com.ldc.design.pattern.creational.prototype.clone.Pig@27973e9b
Pig{name=‘佩奇’ birthday=Thu Jan 01 08:00:00 CST 1970}com.ldc.design.pattern.creational.prototype.clone.Pig@312b1dae
现在,我们来修改pig1的生日:
我们预期是只修改pig1的时间:
public class Test {
public static void main(String[]args) throws CloneNotSupportedException {
Date birthday = new Date(0L);
Pig pig1 = new Pig("佩奇" birthday);
Pig pig2 = (Pig) pig1.clone();
System.out.println(pig1);
System.out.println(pig2);
pig1.getBirthday().setTime(666666666L);
System.out.println(pig1);
System.out.println(pig2);
}
}
12345678910111213
但是实际的结果是把两个对象的时间都进行了修改,这个就是和我们的预期就是不一样了:
Pig{name=‘佩奇’ birthday=Thu Jan 01 08:00:00 CST 1970}com.ldc.design.pattern.creational.prototype.clone.Pig@27973e9b
Pig{name=‘佩奇’ birthday=Thu Jan 01 08:00:00 CST 1970}com.ldc.design.pattern.creational.prototype.clone.Pig@312b1dae
Pig{name=‘佩奇’ birthday=Fri Jan 09 01:11:06 CST 1970}com.ldc.design.pattern.creational.prototype.clone.Pig@27973e9b
Pig{name=‘佩奇’ birthday=Fri Jan 09 01:11:06 CST 1970}com.ldc.design.pattern.creational.prototype.clone.Pig@312b1dae
这种默认的克隆方式就是一个浅克隆的方式;
现在,我们来实现一个深克隆:
在Pig的这个类里面实现这个方法:
@Override
protected Object clone() throws CloneNotSupportedException {
Pig pig = (Pig) super.clone();
/** 深克隆 */
pig.birthday = (Date) pig.birthday.clone();
return pig;
}
1234567
Pig的这个类如下:
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 {
Pig pig = (Pig) super.clone();
/** 深克隆 */
pig.birthday = (Date) pig.birthday.clone();
return pig;
}
@Override
public String toString() {
return "Pig{"
"name='" name '\''
" birthday=" birthday
'}' super.toString();
}
}
1234567891011121314151617181920212223242526272829303132333435363738394041
这个时候,日期对象就不是同一个对象了:
这个时候,输出就符合了我们的预期了:只修改了Pig1
Pig{name=‘佩奇’ birthday=Thu Jan 01 08:00:00 CST 1970}com.ldc.design.pattern.creational.prototype.clone.Pig@34ce8af7
Pig{name=‘佩奇’ birthday=Thu Jan 01 08:00:00 CST 1970}com.ldc.design.pattern.creational.prototype.clone.Pig@b684286
Pig{name=‘佩奇’ birthday=Fri Jan 09 01:11:06 CST 1970}com.ldc.design.pattern.creational.prototype.clone.Pig@34ce8af7
Pig{name=‘佩奇’ birthday=Thu Jan 01 08:00:00 CST 1970}com.ldc.design.pattern.creational.prototype.clone.Pig@b684286
总结:对于引用类型的变量,我们一定是注意是否要深克隆它,对于引用类型,我们还是建议克隆出来为好,否则就是留了一个隐患;
9-3 原型模式coding-克隆破坏单例
让之前写的单例模式实现Cloneable接口:
public class HungrySingleton implements Serializable Cloneable{
private final static HungrySingleton hungrySingleton;
static {
hungrySingleton = new HungrySingleton();
}
private HungrySingleton() {
if (hungrySingleton != null) {
throw new RuntimeException("单例构造器禁止反射调用");
}
}
public static HungrySingleton getInstance() {
return hungrySingleton;
}
/** 我们加上这样的一个方法 */
private Object readResolve() {
return hungrySingleton;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
1234567891011121314151617181920212223
测试:
public class Test {
public static void main(String[]args) throws CloneNotSupportedException NoSuchMethodException InvocationTargetException IllegalAccessException {
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);
}
}
12345678910
运行结果:
com.ldc.design.pattern.creational.singleton.HungrySingleton@34c45dca
com.ldc.design.pattern.creational.singleton.HungrySingleton@52cc8049
如果我们想要克隆不破坏单例,那么我们可以这样做:
public class HungrySingleton implements Serializable Cloneable{
private final static HungrySingleton hungrySingleton;
static {
hungrySingleton = new HungrySingleton();
}
private HungrySingleton() {
if (hungrySingleton != null) {
throw new RuntimeException("单例构造器禁止反射调用");
}
}
public static HungrySingleton getInstance() {
return hungrySingleton;
}
/** 我们加上这样的一个方法 */
private Object readResolve() {
return hungrySingleton;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return getInstance();
}
}
1234567891011121314151617181920212223
现在的运行结果:这个时候,是同一个对象,还是单例的
com.ldc.design.pattern.creational.singleton.HungrySingleton@34c45dca
com.ldc.design.pattern.creational.singleton.HungrySingleton@34c45dca
如何防止克隆不破坏单例模式:
1.要么不实现Cloneable接口
2.要么实现了Cloneable接口,但是在重写的方法clone方法里面获得对象的实例
这样的话,就不怕克隆破坏单例模式了;
9-4 原型模式源码解析
我们查看一下它的实现:
那它是如何重写的呢?