定义及分类

定义

对象拷贝就是将一个对象的属性拷贝到两一个有着相同类型的对象中去,在程序中拷贝对象时很常见的,主要是为了在新的上下文环境中复用对象的部分或者全部数据

分类

浅拷贝
深拷贝
延迟拷贝

浅拷贝

什么是浅拷贝

浅拷贝是按位拷贝对象,他会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝 or 浅拷贝 - 图1
在上图中,SourceObject有一个int类型的属性 “field1”和一个引用类型属性”refObj”(引用ContainedObject类型的对象)。当对SourceObject做浅拷贝时,创建了CopiedObject,它有一个包含”field1”拷贝值的属性”field2”以及仍指向refObj本身的引用。由于”field1”是基本类型,所以只是将它的值拷贝给”field2”,但是由于”refObj”是一个引用类型, 所以CopiedObject指向”refObj”相同的地址。因此对SourceObject中的”refObj”所做的任何改变都会影响到CopiedObject。

  1. @Data
  2. public class Subject {
  3. private String name;
  4. public Subject(String name) {
  5. name = name;
  6. }
  7. }
  8. @Data
  9. public class Student implements Cloneable {
  10. // 对象引用
  11. private Subject subj;
  12. private String name;
  13. public Student(String s, String sub) {
  14. name = s;
  15. subj = new Subject(sub);
  16. }
  17. /**
  18. * 重写clone()方法
  19. * @return
  20. */
  21. public Object clone() {
  22. //浅拷贝
  23. try {
  24. // 直接调用父类的clone()方法
  25. return super.clone();
  26. } catch (CloneNotSupportedException e) {
  27. return null;
  28. }
  29. }
  30. }
  31. public static void main(String[] args){
  32. // 原始对象
  33. Student stud = new Student("杨充", "潇湘剑雨");
  34. System.out.println("原始对象: " + stud.getName() + " - " + stud.getSubj().getName());
  35. // 拷贝对象
  36. Student clonedStud = (Student) stud.clone();
  37. System.out.println("拷贝对象: " + clonedStud.getName() + " - " + clonedStud.getSubj().getName());
  38. // 原始对象和拷贝对象是否一样:
  39. System.out.println("原始对象和拷贝对象是否一样: " + (stud == clonedStud));
  40. // 原始对象和拷贝对象的name属性是否一样
  41. System.out.println("原始对象和拷贝对象的name属性是否一样: " + (stud.getName() == clonedStud.getName()));
  42. // 原始对象和拷贝对象的subj属性是否一样
  43. System.out.println("原始对象和拷贝对象的subj属性是否一样: " + (stud.getSubj() == clonedStud.getSubj()));
  44. stud.setName("小杨逗比");
  45. stud.getSubj().setName("潇湘剑雨大侠");
  46. System.out.println("更新后的原始对象: " + stud.getName() + " - " + stud.getSubj().getName());
  47. System.out.println("更新原始对象后的克隆对象: " + clonedStud.getName() + " - " + clonedStud.getSubj().getName());
  48. }
  49. 2019-03-23 13:50:57.518 24704-24704/com.ycbjie.other I/System.out: 原始对象: 杨充 - 潇湘剑雨
  50. 2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 拷贝对象: 杨充 - 潇湘剑雨
  51. 2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 原始对象和拷贝对象是否一样: false
  52. 2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 原始对象和拷贝对象的name属性是否一样: true
  53. 2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 原始对象和拷贝对象的subj属性是否一样: true
  54. 2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 更新后的原始对象: 小杨逗比 - 潇湘剑雨大侠
  55. 2019-03-23 13:50:57.519 24704-24704/com.ycbjie.other I/System.out: 更新原始对象后的克隆对象: 杨充 - 潇湘剑雨大侠
  56. 作者:杨充
  57. 链接:https://juejin.im/post/5c988a7ef265da6116246d11
  58. 来源:掘金
  59. 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

深拷贝

什么是深拷贝

深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。
深拷贝 or 浅拷贝 - 图2
在上图中,SourceObject有一个int类型的属性 “field1”和一个引用类型属性”refObj1”(引用ContainedObject类型的对象)。当对SourceObject做深拷贝时,创建了CopiedObject,它有一个包含”field1”拷贝值的属性”field2”以及包含”refObj1”拷贝值的引用类型属性”refObj2” 。因此对SourceObject中的”refObj”所做的任何改变都不会影响到CopiedObject

  1. @Data
  2. public class Student implements Cloneable {
  3. // 对象引用
  4. private Subject subj;
  5. private String name;
  6. public Student(String s, String sub) {
  7. name = s;
  8. subj = new Subject(sub);
  9. }
  10. /**
  11. * 重写clone()方法
  12. *
  13. * @return
  14. */
  15. public Object clone() {
  16. // 深拷贝,创建拷贝类的一个新对象,这样就和原始对象相互独立
  17. Student s = new Student(name, subj.getName());
  18. return s;
  19. }
  20. }

延迟拷贝

  • 延迟拷贝是浅拷贝和深拷贝的一个组合,实际上很少会使用。这个以前几乎都没听说过,后来看书才知道有这么一种拷贝!
  • 当最开始拷贝一个对象时,会使用速度较快的浅拷贝,还会使用一个计数器来记录有多少对象共享这个数据。当程序想要修改原始的对象时,它会决定数据是否被共享(通过检查计数器)并根据需要进行深拷贝。
  • 延迟拷贝从外面看起来就是深拷贝,但是只要有可能它就会利用浅拷贝的速度。当原始对象中的引用不经常改变的时候可以使用延迟拷贝。由于存在计数器,效率下降很高,但只是常量级的开销。而且, 在某些情况下, 循环引用会导致一些问题。

https://juejin.im/post/5c988a7ef265da6116246d11