定义

原型实例指定创建对象的种类,通过拷贝这些原型创建新的对象

不需要知道任何创建的细节,不需要用到构造函数

简化创建对象过程,假设有一个类new对象的是比较耗资源的,那么原型模式就比较提高效率了

核心是clone方法

如果抽象类实现Cloneable接口并重写clone方法,那么子类继承此抽象类就可以直接使用原型模式

原型模式可以破坏单例模式,所以单例模式类不要实现Cloneable接口

demo设计

发邮件,根据邮件实体类Email连续发10封邮件 ,但是不需要new10个对象

实体类

Mail邮件实体类,注意要实现Cloneable接口,重写Object的clone方法

  1. @Data
  2. public class Mail implements Cloneable {
  3. private String name;
  4. private String emailAddress;
  5. private String content;
  6. public Mail() {
  7. System.out.println("Mail class construct");
  8. }
  9. @Override
  10. protected Object clone() throws CloneNotSupportedException {
  11. return super.clone();
  12. }
  13. }

发生邮件的工具类

  1. public class MailUtil {
  2. public static void sendMail(Mail mail) {
  3. String outputContent = "向{0},邮件地址:{1},邮件内容:{2}发送邮件";
  4. System.out.println(MessageFormat.format(outputContent, mail.getName(), mail.getEmailAddress(), mail.getContent()));
  5. }
  6. public static void saveOriginMailRecord(Mail mail) {
  7. System.out.println("存储mail记录: " + mail);
  8. }
  9. }

测试方法

  1. public static void main(String[] args) throws CloneNotSupportedException {
  2. Mail mail = new Mail();
  3. mail.setContent("初始化模板");
  4. for (int i = 0; i < 10; i++) {
  5. //通过clone创建类
  6. Mail tempMail = (Mail) mail.clone();
  7. tempMail.setName(i + "同学");
  8. tempMail.setEmailAddress(i + "同学" + "@qq.com");
  9. tempMail.setContent("恭喜" + i + "同学中奖");
  10. MailUtil.sendMail(tempMail);
  11. }
  12. MailUtil.saveOriginMailRecord(mail);
  13. }

会出现的问题及解决方案

问题 浅拷贝

假设有一个pig类

  1. @Data
  2. @NoArgsConstructor
  3. @AllArgsConstructor
  4. public class Pig implements Cloneable {
  5. private String name;
  6. private Date birthday;
  7. @Override
  8. protected Object clone() throws CloneNotSupportedException {
  9. return super.clone();
  10. }
  11. }

类里所用的属性有引用类型

  1. public static void main(String[] args) throws CloneNotSupportedException {
  2. Date birthday = new Date(0L);
  3. Pig pig1 = new Pig("1", birthday);
  4. Pig pig2 = (Pig) pig1.clone();
  5. System.out.println(pig1);
  6. System.out.println(pig2);
  7. //引用的是同一个对象
  8. pig1.getBirthday().setTime(6666666666L);
  9. System.out.println(pig1);
  10. System.out.println(pig2);
  11. }

查看运行信息,会发现pig1对象的属性更改后,pig2的属性也更改了
image.png
clone出来的对象的引用类型的属性指向的是同一个对象

解决方案 完成深拷贝

在实体类中把每一个引用类型也进行clone

  1. @Data
  2. @NoArgsConstructor
  3. @AllArgsConstructor
  4. public class Pig implements Cloneable {
  5. private String name;
  6. private Date birthday;
  7. @Override
  8. protected Object clone() throws CloneNotSupportedException {
  9. Pig pig = (Pig) super.clone();
  10. //对每个引用对象单独clone才能实现深克隆 引用的就不是同一个对象了
  11. pig.birthday = (Date) pig.birthday.clone();
  12. return pig;
  13. }
  14. }