原型模式是什么?

利用一个对象做原型,拷贝生成多个相似的对象叫做原型模式。
是创建复杂对象的一种快速高效的方式。

适用场景

复杂对象,new对象耗时,使用原型模式进行拷贝,提高速度

代码实现

最简单的原型模式

JDK默认提供了原型模式,java.lang.Cloneable接口就是一个原型模式
实现java.lang.Cloneable 方法,重写clone()方法即可实现原型模式,注意两点:

  1. clone()方法是java.lang.Object类提供的,每个对象都默认继承了这个方法,且这个方法是native实现

image.png

  1. 如果不实现java.lang.Cloneable 接口,会报错java.lang.CloneNotSupportedException异常

来一个最简单的原型模式代码实现:

  1. /**
  2. * 原型模式
  3. * 原型类 实现JDK的Cloneable接口即可
  4. */
  5. class Prototype implements Cloneable {
  6. //具有代表性类型的属性
  7. private Integer i;//基本类型
  8. private String s;//字符串类型
  9. @Override
  10. protected Object clone() throws CloneNotSupportedException {
  11. return super.clone();
  12. }
  13. }
  14. //测试类
  15. public class PrototypeTest {
  16. public static void main(String[] args) throws InterruptedException, CloneNotSupportedException {
  17. Prototype p1 = new Prototype();
  18. Object p2 = p1.clone();
  19. System.out.println("p1==p2:" + (p1 == p2));
  20. //输出:p1==p2:false
  21. }
  22. }

实现Cloneable接口并重写clone(),JDK默认就帮我们实现了原型模式,这就是我们常说的“浅拷贝”,只复制基本类型的数据,引用类型的数据只复制了引用的地址,引用的对象并没有复制,在新的对象中修改引用类型的数据会影响原对象中的引用。

  1. 对于基本类型,拷贝其值
  2. 对于引用类型,拷贝其引用,但引用的对象是同一个

    浅拷贝-基本类型

    基本类型全拷贝,无影响 ```java //具体原型类 class Prototype implements Cloneable {

    //具有代表性类型的属性 private Integer i;//基本类型 private String s;//字符串类型

    @Override protected Object clone() throws CloneNotSupportedException {

    1. return super.clone();

    }

    //构造方法 public Prototype(Integer i, String s) {

    1. this.i = i;
    2. this.s = s;

    }

    //—-geter和seter方法 public Integer getI() {

    1. return i;

    }

    public void setI(Integer i) {

    1. this.i = i;

    }

    public String getS() {

    1. return s;

    }

    public void setS(String s) {

    1. this.s = s;

    }

  1. //重写toString方法 方便查看对象
  2. @Override
  3. public String toString() {
  4. return "Prototype{" +
  5. "i=" + i +
  6. ", s='" + s + '\'' +
  7. '}';
  8. }

}

//测试类 public class PrototypeTest { public static void main(String[] args) throws InterruptedException, CloneNotSupportedException { Prototype p1 = new Prototype(1, “对象1”); Prototype p2 = (Prototype) p1.clone();

  1. System.out.println("p1 == p2:" + (p1 == p2));
  2. System.out.println("p1:" + p1);
  3. System.out.println("p2:" + p2);
  4. //输出:
  5. //p1 == p2:false
  6. //p1:Prototype{i=1, s='对象1'}
  7. //p2:Prototype{i=1, s='对象1'}
  8. }

}

  1. <a name="AsCtP"></a>
  2. ## 浅拷贝-引用对象
  3. 在刚才的类的集成上增加一个引用对象 Other
  4. ```java
  5. //具体原型类
  6. class Prototype implements Cloneable {
  7. //具有代表性类型的属性
  8. private Integer i;//基本类型
  9. private String s;//字符串类型
  10. private Other other;//引用类型
  11. @Override
  12. protected Object clone() throws CloneNotSupportedException {
  13. return super.clone();
  14. }
  15. //构造方法
  16. public Prototype(Integer i, String s, Other other) throws InterruptedException {
  17. this.i = i;
  18. this.s = s;
  19. this.other = other;
  20. }
  21. //---geter和seter方法
  22. public Integer getI() {
  23. return i;
  24. }
  25. public void setI(Integer i) {
  26. this.i = i;
  27. }
  28. public String getS() {
  29. return s;
  30. }
  31. public void setS(String s) {
  32. this.s = s;
  33. }
  34. public Other getOther() {
  35. return other;
  36. }
  37. public void setOther(Other other) {
  38. this.other = other;
  39. }
  40. //重写toString方法 方便查看对象
  41. @Override
  42. public String toString() {
  43. return "Prototype{" +
  44. "i=" + i +
  45. ", s='" + s + '\'' +
  46. ", other=" + other +
  47. '}';
  48. }
  49. }
  50. class Other {
  51. private int id;
  52. private String name;
  53. public Other(int id, String name) {
  54. this.id = id;
  55. this.name = name;
  56. }
  57. public int getId() {
  58. return id;
  59. }
  60. public void setId(int id) {
  61. this.id = id;
  62. }
  63. public String getName() {
  64. return name;
  65. }
  66. public void setName(String name) {
  67. this.name = name;
  68. }
  69. @Override
  70. public String toString() {
  71. return "Other{" +
  72. "id=" + id +
  73. ", name='" + name + '\'' +
  74. '}';
  75. }
  76. }
  77. //测试类
  78. public class PrototypeTest {
  79. public static void main(String[] args) throws InterruptedException, CloneNotSupportedException, IOException, ClassNotFoundException {
  80. Prototype p1 = new Prototype(1, "对象1", new Other(11, "对象11"));
  81. Prototype p2 = (Prototype) p1.clone();
  82. Other other1 = p1.getOther();
  83. Other other2 = p2.getOther();
  84. System.out.println("p1 == p2:" + (p1 == p2));
  85. System.out.println(p1);
  86. System.out.println(p2);
  87. System.out.println("sub1 == sub2:" + (other1 == other2));
  88. System.out.println("sub1:" + other1);
  89. System.out.println("sub2:" + other2);
  90. p1.getOther().setId(3333);
  91. System.out.println(p1);
  92. System.out.println(p2);
  93. //不显示指定clone()方法 则为浅拷贝
  94. //输出:
  95. //p1 == p2:false
  96. //Prototype{i=1, s='对象1', other=Other{id=11, name='对象11'}}
  97. //Prototype{i=1, s='对象1', other=Other{id=11, name='对象11'}}
  98. //sub1 == sub2:true
  99. //sub1:Other{id=11, name='对象11'}
  100. //sub2:Other{id=11, name='对象11'}
  101. //Prototype{i=1, s='对象1', other=Other{id=3333, name='对象11'}}
  102. //Prototype{i=1, s='对象1', other=Other{id=3333, name='对象11'}}
  103. }
  104. }

深拷贝-重写clone()方式

  • 所有的类都需要实现Cloneable,并显示指定clone()方法
  • 核心代码

    1. @Override
    2. protected Object clone() throws CloneNotSupportedException {
    3. Prototype prototype = (Prototype) super.clone();
    4. //需要显示指定所有引用对象的clone()方法
    5. prototype.other = (Other) other.clone();
    6. return prototype;
    7. }

    ```java //具体原型类 class Prototype implements Cloneable {

    //具有代表性类型的属性 private Integer i;//基本类型 private String s;//字符串类型 private Other other;//引用类型

    @Override protected Object clone() throws CloneNotSupportedException {

    1. Prototype prototype = (Prototype) super.clone();
    2. //需要显示指定所有引用对象的clone()方法
    3. prototype.other = (Other) other.clone();
    4. return prototype;

    }

    //构造方法 public Prototype(Integer i, String s, Other other) throws InterruptedException {

    1. this.i = i;
    2. this.s = s;
    3. this.other = other;

    }

    //—-geter和seter方法 public Integer getI() {

    1. return i;

    }

    public void setI(Integer i) {

    1. this.i = i;

    }

    public String getS() {

    1. return s;

    }

    public void setS(String s) {

    1. this.s = s;

    }

    public Other getOther() {

    1. return other;

    }

    public void setOther(Other other) {

    1. this.other = other;

    }

  1. //重写toString方法 方便查看对象
  2. @Override
  3. public String toString() {
  4. return "Prototype{" +
  5. "i=" + i +
  6. ", s='" + s + '\'' +
  7. ", other=" + other +
  8. '}';
  9. }

}

class Other implements Cloneable { private int id; private String name;

  1. @Override
  2. protected Object clone() throws CloneNotSupportedException {
  3. return super.clone();
  4. }
  5. public Other(int id, String name) {
  6. this.id = id;
  7. this.name = name;
  8. }
  9. public int getId() {
  10. return id;
  11. }
  12. public void setId(int id) {
  13. this.id = id;
  14. }
  15. public String getName() {
  16. return name;
  17. }
  18. public void setName(String name) {
  19. this.name = name;
  20. }
  21. @Override
  22. public String toString() {
  23. return "Other{" +
  24. "id=" + id +
  25. ", name='" + name + '\'' +
  26. '}';
  27. }

}

//测试类 public class PrototypeTest { public static void main(String[] args) throws InterruptedException, CloneNotSupportedException, IOException, ClassNotFoundException { Prototype p1 = new Prototype(1, “对象1”, new Other(11, “对象11”)); Prototype p2 = (Prototype) p1.clone();

  1. Other other1 = p1.getOther();
  2. Other other2 = p2.getOther();
  3. System.out.println("p1 == p2:" + (p1 == p2));
  4. System.out.println(p1);
  5. System.out.println(p2);
  6. System.out.println("sub1 == sub2:" + (other1 == other2));
  7. System.out.println("sub1:" + other1);
  8. System.out.println("sub2:" + other2);
  9. p1.getOther().setId(3333);
  10. System.out.println(p1);
  11. System.out.println(p2);
  12. //显示指定clone()方法 则为深拷贝
  13. //输出:
  14. //p1 == p2:false
  15. //Prototype{i=1, s='对象1', other=Other{id=11, name='对象11'}}
  16. //Prototype{i=1, s='对象1', other=Other{id=11, name='对象11'}}
  17. //sub1 == sub2:false
  18. //sub1:Other{id=11, name='对象11'}
  19. //sub2:Other{id=11, name='对象11'}
  20. //Prototype{i=1, s='对象1', other=Other{id=3333, name='对象11'}}
  21. //Prototype{i=1, s='对象1', other=Other{id=11, name='对象11'}}
  22. }

}

  1. **个人的理解是Java中定义的clone没有深浅之分,都是统一的调用Objectclone方法。为什么会有深克隆的概念?是由于我们在实现的过程中刻意的嵌套了clone方法的调用。也就是说深克隆就是在需要克隆的对象类型的类中全部实现克隆方法。就像是toString方法一样。**<br />总结:<br />1.浅克隆:只复制基本类型的数据,引用类型的数据只复制了引用的地址,引用的对象并没有复制,在新的对象中修改引用类型的数据会影响原对象中的引用。<br />2.深克隆:是在引用类型的类中也实现了clone,是clone的嵌套,复制后的对象与原对象之间完全不会影响。<br />4.使用clone实现的深克隆其实是浅克隆中嵌套了浅克隆,与toString方法类似<br />_
  2. <a name="drDHE"></a>
  3. ## 深拷贝-序列化方式
  4. 使用对象序列化方式,把一个对象写入内存,然后从内存重写读取,拷贝出另一个对象,也可以实现深拷贝。
  5. ```java
  6. //具体原型类
  7. class Prototype implements Serializable {
  8. //具有代表性类型的属性
  9. private Integer i;//基本类型
  10. private String s;//字符串类型
  11. private Other other;//引用类型
  12. //构造方法
  13. public Prototype(Integer i, String s, Other other) {
  14. this.i = i;
  15. this.s = s;
  16. this.other = other;
  17. }
  18. //---geter和seter方法
  19. public Integer getI() {
  20. return i;
  21. }
  22. public void setI(Integer i) {
  23. this.i = i;
  24. }
  25. public String getS() {
  26. return s;
  27. }
  28. public void setS(String s) {
  29. this.s = s;
  30. }
  31. public Other getOther() {
  32. return other;
  33. }
  34. public void setOther(Other other) {
  35. this.other = other;
  36. }
  37. //重写toString方法 方便查看对象
  38. @Override
  39. public String toString() {
  40. return "Prototype{" +
  41. "i=" + i +
  42. ", s='" + s + '\'' +
  43. ", other=" + other +
  44. '}';
  45. }
  46. }
  47. class Other implements Serializable {
  48. private int id;
  49. private String name;
  50. public Other(int id, String name) {
  51. this.id = id;
  52. this.name = name;
  53. }
  54. public int getId() {
  55. return id;
  56. }
  57. public void setId(int id) {
  58. this.id = id;
  59. }
  60. public String getName() {
  61. return name;
  62. }
  63. public void setName(String name) {
  64. this.name = name;
  65. }
  66. @Override
  67. public String toString() {
  68. return "Other{" +
  69. "id=" + id +
  70. ", name='" + name + '\'' +
  71. '}';
  72. }
  73. }
  74. //测试类
  75. public class PrototypeTest {
  76. public static void main(String[] args) throws InterruptedException, CloneNotSupportedException, IOException, ClassNotFoundException {
  77. //实现深拷贝
  78. Prototype p1 = new Prototype(1, "对象1", new Other(11, "对象11"));
  79. //将对象写入内存
  80. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  81. new ObjectOutputStream(bos).writeObject(p1);
  82. //从内存读取对象
  83. Prototype p2 = (Prototype) new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())).readObject();
  84. System.out.println();
  85. System.out.println("p1 == p2:" + (p1 == p2));
  86. System.out.println(p1);
  87. System.out.println(p2);
  88. Other other1 = p1.getOther();
  89. Other other2 = p2.getOther();
  90. System.out.println("sub1 == sub2:" + (other1 == other2));//深拷贝 引用对象也是重新拷贝一个新的对象
  91. System.out.println("sub1:" + other1);
  92. System.out.println("sub2:" + other2);
  93. p1.getOther().setId(3333);
  94. System.out.println(p1);
  95. System.out.println(p2);
  96. //输出:
  97. //p1 == p2:false
  98. //Prototype{i=1, s='对象1', other=Other{id=3333, name='对象11'}}
  99. //Prototype{i=1, s='对象1', other=Other{id=11, name='对象11'}}
  100. //sub1 == sub2:false
  101. //sub1:Other{id=3333, name='对象11'}
  102. //sub2:Other{id=11, name='对象11'}
  103. //Prototype{i=1, s='对象1', other=Other{id=3333, name='对象11'}}
  104. //Prototype{i=1, s='对象1', other=Other{id=11, name='对象11'}}
  105. }
  106. }
  • 所有类都实现Serializable接口,使其可序列化
  • 对象写入内存

    1. ByteArrayOutputStream bos = new ByteArrayOutputStream();
    2. new ObjectOutputStream(bos).writeObject(p1);
  • 复制对象的过程就是反序列化的过程

    1. Prototype p2 = (Prototype) new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())).readObject();

    深拷贝-耗时测试

    深拷贝可以节省时间?做个测试
    模拟创建对象的时候耗时操作

    1. //模拟创建对象耗时操作
    2. try {
    3. Thread.sleep(3_000);
    4. } catch (InterruptedException e) {
    5. e.printStackTrace();
    6. }

    ```java //具体原型类 class Prototype implements Serializable {

    //具有代表性类型的属性 private Integer i;//基本类型 private String s;//字符串类型 private Other other;//引用类型

  1. //构造方法
  2. public Prototype(Integer i, String s, Other other) {
  3. this.i = i;
  4. this.s = s;
  5. this.other = other;
  6. //模拟创建对象耗时操作
  7. try {
  8. Thread.sleep(3_000);
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. }
  13. //---geter和seter方法
  14. public Integer getI() {
  15. return i;
  16. }
  17. public void setI(Integer i) {
  18. this.i = i;
  19. }
  20. public String getS() {
  21. return s;
  22. }
  23. public void setS(String s) {
  24. this.s = s;
  25. }
  26. public Other getOther() {
  27. return other;
  28. }
  29. public void setOther(Other other) {
  30. this.other = other;
  31. }
  32. //重写toString方法 方便查看对象
  33. @Override
  34. public String toString() {
  35. return "Prototype{" +
  36. "i=" + i +
  37. ", s='" + s + '\'' +
  38. ", other=" + other +
  39. '}';
  40. }

}

class Other implements Serializable { private int id; private String name;

  1. public Other(int id, String name) {
  2. this.id = id;
  3. this.name = name;
  4. }
  5. public int getId() {
  6. return id;
  7. }
  8. public void setId(int id) {
  9. this.id = id;
  10. }
  11. public String getName() {
  12. return name;
  13. }
  14. public void setName(String name) {
  15. this.name = name;
  16. }
  17. @Override
  18. public String toString() {
  19. return "Other{" +
  20. "id=" + id +
  21. ", name='" + name + '\'' +
  22. '}';
  23. }

}

//测试类 public class PrototypeTest { public static void main(String[] args) throws InterruptedException, CloneNotSupportedException, IOException, ClassNotFoundException { long start1 = System.currentTimeMillis(); Prototype p1 = new Prototype(1, “对象1”, new Other(11, “对象11”)); long end1 = System.currentTimeMillis(); System.out.println(“new对象耗时:” + (end1 - start1)); //将对象写入内存 ByteArrayOutputStream bos = new ByteArrayOutputStream(); new ObjectOutputStream(bos).writeObject(p1);

  1. //从内存读取对象
  2. long start2 = System.currentTimeMillis();
  3. Prototype p2 = (Prototype) new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())).readObject();
  4. long end2 = System.currentTimeMillis();
  5. System.out.println("内存拷贝对象耗时:" + (end2 - start2));
  6. }
  7. //输出:
  8. //new对象耗时:3002
  9. //内存拷贝对象耗时:35

``` 总结:

1.浅克隆:只复制基本类型的数据,引用类型的数据只复制了引用的地址,引用的对象并没有复制,在新的对象中修改引用类型的数据会影响原对象中的引用。
2.深克隆:是在引用类型的类中也实现了clone,是clone的嵌套,复制后的对象与原对象之间完全不会影响。
3.使用序列化也能完成深复制的功能:对象序列化后写入流中,此时也就不存在引用什么的概念了,再从流中读取,生成新的对象,新对象和原对象之间也是完全互不影响的。
4.使用clone实现的深克隆其实是浅克隆中嵌套了浅克隆,与toString方法类似