1. Object.clone();
  2. 也叫克隆模式
  3. 想要new一个与原对象一样或者变化很小的对象可以使用原型模式,而不必手动new并且再次赋值了

UML类图

image.png

java中的原型模式

  1. 自带
  2. 实现原型模式需要实现标记型接口Cloneable
  3. 一般会重写clone()方法
    1. 如果重写clone()方法,而没有实现接口,调用时会报异常(因为clone方法是Object基类中的方法故可以重写)
  4. 一般用于一个对象的属性已经确定,需要产生很多相同对象的时候
  5. 需要区分深克隆与浅克隆
  6. 克隆实现的原理:拷贝内存(不调用构造方法,直接拷贝内存)
  7. 克隆方法必须重写:
    1. 因为Object中的clone是protected
    2. 并且是native方法
  8. 调用clone之后还要进行强制类型转换!
  9. 要完成克隆两部:
    1. 实现标记接口Cloneable
    2. 重写clone方法

浅克隆问题

  • 浅克隆两个引用指向同一个对象

    对Person对象进行浅克隆

    ```java package com.mashibing.dp.prototype.v1;

/**

  • 浅克隆 */

public class Test { public static void main(String[] args) throws Exception { Person p1 = new Person(); Person p2 = (Person)p1.clone(); System.out.println(p2.age + “ “ + p2.score); System.out.println(p2.loc);

  1. System.out.println(p1.loc == p2.loc);
  2. p1.loc.street = "sh";
  3. System.out.println(p2.loc);
  4. }

}

class Person implements Cloneable { int age = 8; int score = 100;

  1. Location loc = new Location("bj", 22);
  2. @Override
  3. public Object clone() throws CloneNotSupportedException {
  4. return super.clone();
  5. }

}

class Location { String street; int roomNo;

  1. @Override
  2. public String toString() {
  3. return "Location{" +
  4. "street='" + street + '\'' +
  5. ", roomNo=" + roomNo +
  6. '}';
  7. }
  8. public Location(String street, int roomNo) {
  9. this.street = street;
  10. this.roomNo = roomNo;
  11. }

}

  1. - 将对象中的成员对象也进行clone,调用成员对象的clone方法
  2. <a name="Zd4Fz"></a>
  3. ### 对Person对象进行深克隆===>同时将Location对象克隆
  4. ```java
  5. package com.mashibing.dp.prototype.v2;
  6. /**
  7. * 深克隆的处理
  8. */
  9. public class Test {
  10. public static void main(String[] args) throws Exception {
  11. Person p1 = new Person();
  12. Person p2 = (Person)p1.clone();
  13. System.out.println(p2.age + " " + p2.score);
  14. System.out.println(p2.loc);
  15. System.out.println(p1.loc == p2.loc);
  16. p1.loc.street = "sh";
  17. System.out.println(p2.loc);
  18. }
  19. }
  20. class Person implements Cloneable {
  21. int age = 8;
  22. int score = 100;
  23. Location loc = new Location("bj", 22);
  24. @Override
  25. public Object clone() throws CloneNotSupportedException {
  26. Person p = (Person)super.clone();
  27. // 对Person对象克隆的同时将Location对象克隆
  28. p.loc = (Location)loc.clone();
  29. return p;
  30. }
  31. }
  32. class Location implements Cloneable {
  33. String street;
  34. int roomNo;
  35. @Override
  36. public String toString() {
  37. return "Location{" +
  38. "street='" + street + '\'' +
  39. ", roomNo=" + roomNo +
  40. '}';
  41. }
  42. public Location(String street, int roomNo) {
  43. this.street = street;
  44. this.roomNo = roomNo;
  45. }
  46. @Override
  47. public Object clone() throws CloneNotSupportedException {
  48. return super.clone();
  49. }
  50. }
  • Sting在克隆时需要深克隆吗?
    • 不需要对String进行深克隆
    • 因为String是直接指定的常量值,本来就是共用的
    • 字符串常量池
    • String是不可变类(String是不可变类,还是什么????Serializable可序列化的?这之间有什么关系)
    • 虽然clone出来还是指向一个,但是改其中一个,另一个并不会变化(因为修改String默认指向另一个对象)
    • new String()类似,引用指向堆中对象,队中对象本质上还是指向字符串常量池的常量(疑问?视频prototype原型2中为什么会说new String是需要深克隆,改变一个另一个 会改变)因为指向堆中的同一个对象
    • 确实会改变,但一般代码中很少用new创建String,相当于每次都创建String,不符合实际使用,故用StringBuilder类型
      • 图解
    • image.png
  • v4版本在克隆Location的时候还要把Location中的StringBuilder给克隆了
  • 在使用克隆的时候要考虑的比较多

    String不需要进一步深克隆===>String是不可变类,重新赋值会指向另一个对象

    ```java package com.mashibing.dp.prototype.v3;

/**

  • String需要进一步深克隆吗? */ public class Test { public static void main(String[] args) throws Exception {

    1. Person p1 = new Person();
    2. Person p2 = (Person)p1.clone();
    3. System.out.println(p2.age + " " + p2.score);
    4. System.out.println(p2.loc);
    5. System.out.println(p1.loc == p2.loc);
    6. p1.loc.street = "sh";
    7. System.out.println(p2.loc);
    8. p1.loc.street.replace("sh", "sz");
    9. System.out.println(p2.loc.street);

    } }

class Person implements Cloneable { int age = 8; int score = 100;

  1. Location loc = new Location("bj", 22);
  2. @Override
  3. public Object clone() throws CloneNotSupportedException {
  4. Person p = (Person)super.clone();
  5. p.loc = (Location)loc.clone();
  6. return p;
  7. }

}

class Location implements Cloneable { String street; int roomNo;

  1. @Override
  2. public String toString() {
  3. return "Location{" +
  4. "street='" + street + '\'' +
  5. ", roomNo=" + roomNo +
  6. '}';
  7. }
  8. public Location(String street, int roomNo) {
  9. this.street = street;
  10. this.roomNo = roomNo;
  11. }
  12. @Override
  13. public Object clone() throws CloneNotSupportedException {
  14. return super.clone();
  15. }

}

  1. <a name="zm9xt"></a>
  2. ### StringBuilder需要进一步深克隆
  3. - StringBuilder对象是Person对象中的Location对象中的StringBuilder对象!!!
  4. - 仍然需要深克隆
  5. - 不进行深克隆的话,仍然是浅克隆(虽然Location对象是深克隆,但是StringBuilder仍然是同一个对象)
  6. ```java
  7. package com.mashibing.dp.prototype.v4;
  8. /**
  9. * StringBuilder需要进一步深克隆吗?
  10. */
  11. public class Test {
  12. public static void main(String[] args) throws Exception {
  13. Person p1 = new Person();
  14. Person p2 = (Person)p1.clone();
  15. System.out.println("p1.loc == p2.loc? " + (p1.loc == p2.loc));
  16. p1.loc.street.reverse();
  17. System.out.println(p2.loc.street);
  18. }
  19. }
  20. class Person implements Cloneable {
  21. int age = 8;
  22. int score = 100;
  23. Location loc = new Location(new StringBuilder("bj"), 22);
  24. @Override
  25. public Object clone() throws CloneNotSupportedException {
  26. Person p = (Person)super.clone();
  27. p.loc = (Location)loc.clone();
  28. return p;
  29. }
  30. }
  31. class Location implements Cloneable {
  32. StringBuilder street;
  33. int roomNo;
  34. @Override
  35. public String toString() {
  36. return "Location{" +
  37. "street='" + street + '\'' +
  38. ", roomNo=" + roomNo +
  39. '}';
  40. }
  41. public Location(StringBuilder street, int roomNo) {
  42. this.street = street;
  43. this.roomNo = roomNo;
  44. }
  45. @Override
  46. public Object clone() throws CloneNotSupportedException {
  47. return super.clone();
  48. }
  49. }
  • 当遇到多层的对象的对象时(对象作为成员属性),要在每一层类的clone方法中显式调用对象属性的clone方法(对象属性所对应的类要**重写clone方法并实现Cloneable接口**)

    总结

    • 真正写程序的时候,clone方法极少用到,教学会讲到(用的少,问的少)
    • 极少数的面试官会问到,了解一下即可
    • 序列化当前对象,不需要克隆对象
    • BeanUtil的什么方法的底层是通过反射实现的属性浅(前?)拷贝,在复制大量对象的场景应该有限选择克隆模式???
    • 从效率上来说,clone一个对象不比new一个对象要快,他只是省略了给属性赋值的语句
    • 当一个对象属性多且赋值很麻烦,并且就需要大量同一对象的时候,用原型模式比较方便

🤏随想

  1. 父类方法的访问修饰符为protected,子类的方位修饰符可以改成public
  2. 子类访问修饰符可以比父类访问修饰符宽松
  3. 当遇到多层的对象的对象时(对象作为成员属性),要在每一层类的clone方法中显式调用对象属性的clone方法(对象属性所对应的类要重写clone方法并实现Cloneable接口)