定义

指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
不需要知道任何创建的细节,不调用构造函数

类型:创建型

适用场景

  • 类初始化消耗较多资源
  • new产生的一个对象需要非常繁琐的过程(数据准备、访问权限等
  • 构造函数比较复杂
  • 循环体中生产大量对象时

优缺点

  • 优点
    • 原型模式性能比直接new一个对象性能高
    • 简化创建过程
  • 缺点
    • 必须配备克隆方法(实现Cloneable接口(标记型接口))
    • 对克隆复杂对象或对克隆出的对象进行复杂改造时,容易引入风险
    • 对复杂对象向深拷贝、浅拷贝要运用得当

项目实战

浅克隆

shallow clone,浅克隆是指拷贝对象时仅仅copy对象本身和对象中的基本变量,而不拷贝对象包含的引用类型对象(通过 class 定义的类型等),如Date、Integer等。

实现Cloneable接口

Cloneable是一个标记接口,没有任何方法;在Object类中有clone的实现。

  1. package net.gaox.exercise.clone;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import java.math.BigDecimal;
  6. import java.util.Date;
  7. /**
  8. * <p> </p>
  9. *
  10. * @author gaox·Eric
  11. * @site gaox.net
  12. * @date 2019/11/30 15:19
  13. */
  14. @Data
  15. @AllArgsConstructor
  16. @NoArgsConstructor
  17. public class Audi implements Cloneable {
  18. /**
  19. * 品牌
  20. */
  21. private String brand;
  22. /**
  23. * 出厂时间
  24. */
  25. private Date productionTime;
  26. /**
  27. * 价格
  28. */
  29. private BigDecimal price;
  30. //这里重写clone方法,使用父类的clone方法
  31. @Override
  32. protected Audi clone() throws CloneNotSupportedException {
  33. return (Audi) super.clone();
  34. }
  35. }

但是发现克隆出的对象与原对象的内存地址不同,但是Date的对象是相同的,结果造成修改其中一个伤害到另外一个的安全性。。

深克隆

深克隆(deep clone )不仅copy对象本身,而且copy对象包含的引用指向的所有对象。

举例:对象X中包含对Y的引用,Y中包含对Z的引用。浅拷贝X得到X1,X1中依然包含对Y的引用,Y中依然包含对Z的引用。深拷贝则是对浅拷贝的递归,深拷贝X得到X1,X1中包含对Y1(Y的copy)的引用,Y1中包含对Z1(Z的copy)的引用。

重新写克隆方法,对于简单类型可以很方便的克隆,对于引用类型最好还是使用深克隆! ! !

  1. package net.gaox.exercise.clone;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import java.math.BigDecimal;
  6. import java.util.Date;
  7. /**
  8. * <p> 红旗汽车 </p>
  9. *
  10. * @author gaox·Eric
  11. * @site gaox.net
  12. * @date 2019/11/30 13:39
  13. */
  14. @Data
  15. @AllArgsConstructor
  16. @NoArgsConstructor
  17. public class HongQi implements Cloneable {
  18. /**
  19. * 品牌
  20. */
  21. private String brand;
  22. /**
  23. * 出厂时间
  24. */
  25. private Date productionTime;
  26. /**
  27. * 价格
  28. */
  29. private BigDecimal price;
  30. @Override
  31. protected Object clone() throws CloneNotSupportedException {
  32. HongQi obj = (HongQi) super.clone();
  33. //这里需要对本对象的属性进行深克隆
  34. obj.productionTime = (Date) obj.productionTime.clone();
  35. return obj;
  36. }
  37. }

对比

image.png

  1. 红色线框中是Audi类,克隆之后时间依然指向了同一个对象,可能在后期对我们的程序产生影响。
  2. 绿色线框中是HongQi类,克隆的时间指向不同的对象,避免程序被破坏。

细心的朋友可能还注意到了,价格price属性在克隆前后还是指向的同一个对象,为什么不一同将其克隆重新赋值呢? 在JDK8中BigDecimal继承自Number,也是不可变类型,对其操作就会返回一个新的对象,不影响我们程序的健壮性。

增加对BigDecimal的扩充克隆

  1. @Override
  2. protected HongQi clone() throws CloneNotSupportedException {
  3. HongQi obj = (HongQi) super.clone();
  4. //这里需要对本对象的属性进行深克隆
  5. obj.productionTime = (Date) obj.productionTime.clone();
  6. obj.price = new BigDecimal(obj.price.toString());
  7. return obj;
  8. }