• Java中的深拷贝与浅拷贝


    目录

    一、浅拷贝

    二、深拷贝

    1、基本介绍  

     2、深拷贝的实现方式

    (1)嵌套clone方法

     (2)序列化方式

    三、总结


    一、浅拷贝

            浅拷贝由对象实现cloneable接口和重写clone方法,然后调用一次内部不做改写的clone方法克隆出一个对象,如果源对象内部存在引用类型的成员变量,那么就说该克隆是浅克隆,即对于引用类型属性,只克隆引用,两个对象的引用指向同一块内存地址,即同一个对象。

     示例代码:

    1. class Friend{
    2. private String name;
    3. private String home;
    4. public Friend(String name, String home) {
    5. this.name = name;
    6. this.home = home;
    7. }
    8. public String getName() {
    9. return name;
    10. }
    11. public void setName(String name) {
    12. this.name = name;
    13. }
    14. public String getHome() {
    15. return home;
    16. }
    17. public void setHome(String home) {
    18. this.home = home;
    19. }
    20. @Override
    21. public String toString() {
    22. return "Friend{" +
    23. "name='" + name + '\'' +
    24. ", home='" + home + '\'' +
    25. '}';
    26. }
    27. }
    28. class Person implements Cloneable{
    29. private String name;
    30. private Integer age;
    31. private Friend friend;
    32. public Person(String name, Integer age) {
    33. this.name = name;
    34. this.age = age;
    35. }
    36. public String getName() {
    37. return name;
    38. }
    39. public void setName(String name) {
    40. this.name = name;
    41. }
    42. public Integer getAge() {
    43. return age;
    44. }
    45. public void setAge(Integer age) {
    46. this.age = age;
    47. }
    48. public Friend getFriend() {
    49. return friend;
    50. }
    51. public void setFriend(Friend friend) {
    52. this.friend = friend;
    53. }
    54. @Override
    55. protected Object clone() throws CloneNotSupportedException {
    56. return super.clone();
    57. }
    58. @Override
    59. public String toString() {
    60. return "Person{" +
    61. "name='" + name + '\'' +
    62. ", age=" + age +
    63. ", friend=" + friend +
    64. '}';
    65. }
    66. }
    67. public class BaseOnClone {
    68. public static void main(String[] args) throws CloneNotSupportedException {
    69. Person person1 = new Person("张三", 18);
    70. person1.setFriend(new Friend("f1", "西安"));
    71. System.out.println("初始化的person:");
    72. System.out.println(person1);
    73. Person clonePerson = (Person) person1.clone();
    74. System.out.println("克隆出来的person:");
    75. System.out.println(clonePerson);
    76. clonePerson.setName("tom");
    77. clonePerson.setAge(20);
    78. clonePerson.getFriend().setHome("延安");
    79. System.out.println("修改克隆出来的person:");
    80. System.out.println(clonePerson);
    81. System.out.println("修改克隆出来的person之后,原来的person:");
    82. System.out.println(person1);
    83. }
    84. }

            执行结果如下:

             从执行结果来看,对于克隆之后的clonePerson对象,当修改了它的age属性的值时,clonePerson的age的变化并不会影响到person1的age属性;当修改clonePerson对象的friend属性的home值时,person1也受到了影响。

            这是因为通过浅拷贝复制对象时仅仅只是复制对象本身,包括基本属性,但该对象的属性引用其他对象时,该引用对象不会被复制,其拷贝出来的对象和原对象中属性引用的对象是同一个。因此可以使用深拷贝达到完全复制的目的,使得原对象和拷贝出来的对象之间互不影响。

    二、深拷贝

    1、基本介绍  

            进行深拷贝时,基本数据类型变量和引用类型变量指向的对象都会被复制,即针对引用类型的成员变量真正的复制一份,重新开辟空间保存,这样两个引用类型属性互不影响。

     2、深拷贝的实现方式

    (1)嵌套clone方法

            该方法给引用数据类型的属性也实现Cloneable接口,然后重写clone方法。以上面的浅拷贝例子为例:只需要在Friend中也实现Cloneable接口,然后重写clone()方法,再对Person中的clone()方法进行修改即可。

    1)在Friend中也实现Cloneable接口,然后重写clone()方法

    1. class Friend implements Cloneable{
    2. private String name;
    3. private String home;
    4. public Friend(String name, String home) {
    5. this.name = name;
    6. this.home = home;
    7. }
    8. public String getName() {
    9. return name;
    10. }
    11. public void setName(String name) {
    12. this.name = name;
    13. }
    14. public String getHome() {
    15. return home;
    16. }
    17. public void setHome(String home) {
    18. this.home = home;
    19. }
    20. @Override
    21. protected Object clone() throws CloneNotSupportedException {
    22. return super.clone();
    23. }
    24. @Override
    25. public String toString() {
    26. return "Friend{" +
    27. "name='" + name + '\'' +
    28. ", home='" + home + '\'' +
    29. '}';
    30. }
    31. }

    2) 对Person中的clone()方法进行修改

    1. class Person implements Cloneable{
    2. private String name;
    3. private Integer age;
    4. private Friend friend;
    5. public Person(String name, Integer age) {
    6. this.name = name;
    7. this.age = age;
    8. }
    9. public String getName() {
    10. return name;
    11. }
    12. public void setName(String name) {
    13. this.name = name;
    14. }
    15. public Integer getAge() {
    16. return age;
    17. }
    18. public void setAge(Integer age) {
    19. this.age = age;
    20. }
    21. public Friend getFriend() {
    22. return friend;
    23. }
    24. public void setFriend(Friend friend) {
    25. this.friend = friend;
    26. }
    27. @Override
    28. protected Object clone() throws CloneNotSupportedException {
    29. Person person = (Person) super.clone();
    30. person.friend = (Friend) friend.clone();
    31. return person;
    32. }
    33. @Override
    34. public String toString() {
    35. return "Person{" +
    36. "name='" + name + '\'' +
    37. ", age=" + age +
    38. ", friend=" + friend +
    39. '}';
    40. }
    41. }

    3)完整代码

    1. class Friend implements Cloneable{
    2. private String name;
    3. private String home;
    4. public Friend(String name, String home) {
    5. this.name = name;
    6. this.home = home;
    7. }
    8. public String getName() {
    9. return name;
    10. }
    11. public void setName(String name) {
    12. this.name = name;
    13. }
    14. public String getHome() {
    15. return home;
    16. }
    17. public void setHome(String home) {
    18. this.home = home;
    19. }
    20. @Override
    21. protected Object clone() throws CloneNotSupportedException {
    22. return super.clone();
    23. }
    24. @Override
    25. public String toString() {
    26. return "Friend{" +
    27. "name='" + name + '\'' +
    28. ", home='" + home + '\'' +
    29. '}';
    30. }
    31. }
    32. class Person implements Cloneable{
    33. private String name;
    34. private Integer age;
    35. private Friend friend;
    36. public Person(String name, Integer age) {
    37. this.name = name;
    38. this.age = age;
    39. }
    40. public String getName() {
    41. return name;
    42. }
    43. public void setName(String name) {
    44. this.name = name;
    45. }
    46. public Integer getAge() {
    47. return age;
    48. }
    49. public void setAge(Integer age) {
    50. this.age = age;
    51. }
    52. public Friend getFriend() {
    53. return friend;
    54. }
    55. public void setFriend(Friend friend) {
    56. this.friend = friend;
    57. }
    58. @Override
    59. protected Object clone() throws CloneNotSupportedException {
    60. Person person = (Person) super.clone();
    61. person.friend = (Friend) friend.clone();
    62. return person;
    63. }
    64. @Override
    65. public String toString() {
    66. return "Person{" +
    67. "name='" + name + '\'' +
    68. ", age=" + age +
    69. ", friend=" + friend +
    70. '}';
    71. }
    72. }
    73. public class BaseOnClone {
    74. public static void main(String[] args) throws CloneNotSupportedException {
    75. Person person1 = new Person("张三", 18);
    76. person1.setFriend(new Friend("f1", "西安"));
    77. System.out.println("初始化的person:");
    78. System.out.println(person1);
    79. Person clonePerson = (Person) person1.clone();
    80. System.out.println("克隆出来的person:");
    81. System.out.println(clonePerson);
    82. clonePerson.setName("tom");
    83. clonePerson.setAge(20);
    84. clonePerson.getFriend().setHome("延安");
    85. System.out.println("修改克隆出来的person:");
    86. System.out.println(clonePerson);
    87. System.out.println("修改克隆出来的person之后,原来的person:");
    88. System.out.println(person1);
    89. }
    90. }

             执行结果如下:

           

             从执行结果来看,在进行深拷贝的时候,无论是对基本数据类型 (age) 还是引用数据类型 (Friend) 进行修改,原对象 (Person) 都不会受到影响。 

     (2)序列化方式

            如果当类中的属性存在数组(数组不能实现Cloneable接口)或者属性之间的关系比较复杂时,上面的方法都不能很好的实现深克隆了。序列化的方式是让每个类都实现Serializable接口,然后通过序列化和反序列化操作达到深克隆的目的。步骤如下:

    • 使要序列化的对象和该对象的引用类型成员变量对象的类都实现Serializable接口,
    • 创建一个ByteArrayOutputStream内存数组输出流,创建一个ObjectOutputStream序列化流,并传入内存数组输出流,使用序列化流的writeobject方法将要序列化的对象写入内部数组中
    • 创建一个ByteArrayInputStream内存数组读取流,传入一个读取数据的数组,这个数组通过内存数组输出流的toByteArray方法获得,这个数组里面的数据其实就是已经被序列化成二进制数据的对象。
    • 最后创建一个ObjectInputStream反序列化流,并传入内存数组读取流,使用反序列化流的readobject方法将数组中的对象的信息反序列化出来。由于反序列化出的对象就是一个新的对象,完成了深克隆。

            注意:序列化时通过定义一个private static final long serialVersionUID固定要被序列化对象的版本号,且静态的成员和transient关键字修饰的成员不能被序列化。

            代码如下:

    1. import java.io.*;
    2. class Friend implements Serializable {
    3. private String name;
    4. private String home;
    5. public Friend(String name, String home) {
    6. this.name = name;
    7. this.home = home;
    8. }
    9. public String getName() {
    10. return name;
    11. }
    12. public void setName(String name) {
    13. this.name = name;
    14. }
    15. public String getHome() {
    16. return home;
    17. }
    18. public void setHome(String home) {
    19. this.home = home;
    20. }
    21. @Override
    22. public String toString() {
    23. return "Friend{" +
    24. "name='" + name + '\'' +
    25. ", home='" + home + '\'' +
    26. '}';
    27. }
    28. }
    29. class Person implements Serializable{
    30. private String name;
    31. private Integer age;
    32. private Friend friend;
    33. public Person(String name, Integer age) {
    34. this.name = name;
    35. this.age = age;
    36. }
    37. public String getName() {
    38. return name;
    39. }
    40. public void setName(String name) {
    41. this.name = name;
    42. }
    43. public Integer getAge() {
    44. return age;
    45. }
    46. public void setAge(Integer age) {
    47. this.age = age;
    48. }
    49. public Friend getFriend() {
    50. return friend;
    51. }
    52. public void setFriend(Friend friend) {
    53. this.friend = friend;
    54. }
    55. @Override
    56. public String toString() {
    57. return "Person{" +
    58. "name='" + name + '\'' +
    59. ", age=" + age +
    60. ", friend=" + friend +
    61. '}';
    62. }
    63. }
    64. public class BaseOnClone {
    65. public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
    66. Person person = new Person("张三", 18);
    67. person.setFriend(new Friend("f1", "西安"));
    68. System.out.println("初始化的person:");
    69. System.out.println(person);
    70. ByteArrayOutputStream bos = new ByteArrayOutputStream();
    71. ObjectOutputStream oos = new ObjectOutputStream(bos);
    72. oos.writeObject(person);
    73. ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
    74. ObjectInputStream ois = new ObjectInputStream(bis);
    75. Person clonePerson = (Person) ois.readObject();
    76. System.out.println("克隆出来的person:");
    77. System.out.println(clonePerson);
    78. clonePerson.setName("tom");
    79. clonePerson.setAge(24);
    80. clonePerson.getFriend().setHome("长春");
    81. System.out.println("修改克隆出来的person:");
    82. System.out.println(clonePerson);
    83. System.out.println("修改克隆出来的person之后,原来的person:");
    84. System.out.println(person);
    85. }
    86. }

            执行结果如下:

             

            从执行结果来看,序列化的方式也实现了深拷贝。在使用序列化实现深拷贝时,将对象进行序列化之后写入流中,然后通过反序列化读取流,生成新对象,而新生成的对象和原对象之间互不影响。

    三、总结

            对于浅拷贝而言,在复制对象时仅仅复制对象本身,包括基本属性和引用类型,拷贝出来的对象和原对象中属性所引用的对象是同一个。

            对于深拷贝而言,复制之后的对象和原对象之间互不影响。

  • 相关阅读:
    Java调用Web Service接口
    ChatGPT将引发网络安全三大革命
    模块化与单片化优缺点解析:为什么单片链仍是 DeFi 协议的最好选择?
    阿里版ChatGPT:通义千问pk文心一言
    GBase 8s是否带集群模块并实现负载均衡
    三极管原理介绍
    【win 10】Chrome 添加YouTube™ 双字幕插件
    企业工程项目管理系统源码(三控:进度组织、质量安全、预算资金成本、二平台:招采、设计管理)
    Netty简介及简单客户端/服务端示例代码
    [NOI2019]斗主地
  • 原文地址:https://blog.csdn.net/weixin_47382783/article/details/126398991