快捷搜索:  汽车  科技

原型模式代码(原来可以这么简单)

原型模式代码(原来可以这么简单)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-1 原型模式讲解

原型模式代码(原来可以这么简单)(1)


原型模式代码(原来可以这么简单)(2)


原型模式代码(原来可以这么简单)(3)


原型模式代码(原来可以这么简单)(4)


原型模式代码(原来可以这么简单)(5)


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


原型模式代码(原来可以这么简单)(6)


这种默认的克隆方式就是一个浅克隆的方式;


现在,我们来实现一个深克隆:
在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

这个时候,日期对象就不是同一个对象了:

原型模式代码(原来可以这么简单)(7)


这个时候,输出就符合了我们的预期了:只修改了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 原型模式源码解析

原型模式代码(原来可以这么简单)(8)


原型模式代码(原来可以这么简单)(9)


我们查看一下它的实现:

原型模式代码(原来可以这么简单)(10)


原型模式代码(原来可以这么简单)(11)


那它是如何重写的呢?

原型模式代码(原来可以这么简单)(12)

猜您喜欢: