java中关于深拷贝的几种方式

不念不忘少年蓝@ 2024-03-16 09:47 70阅读 0赞

在java里,当我们需要拷贝一个对象时,有两种类型的拷贝:浅拷贝与深拷贝。

  • 浅拷贝只是拷贝了源对象的地址,所以源对象的值发生变化时,拷贝对象的值也会发生变化。
  • 深拷贝则是拷贝了源对象的所有值,所以即使源对象的值发生变化时,拷贝对象的值也不会改变。

方式1:构造函数深拷贝

  1. package com.lyj.demo.pojo.cloneTest;
  2. import lombok.Getter;
  3. /**
  4. * @author 凌兮
  5. * @date 2021/4/15 14:28
  6. * 通过构造器进行深拷贝测试
  7. */
  8. @Getter
  9. public class UserConstruct {
  10. private String userName;
  11. private AddressConstruct address;
  12. public UserConstruct() {
  13. }
  14. public UserConstruct(String userName, AddressConstruct address) {
  15. this.userName = userName;
  16. this.address = address;
  17. }
  18. public static void main(String[] args) {
  19. AddressConstruct address = new AddressConstruct("小区1", "小区2");
  20. UserConstruct user = new UserConstruct("小李", address);
  21. // 调用构造函数进行深拷贝
  22. UserConstruct copyUser = new UserConstruct(user.getUserName(), new AddressConstruct(address.getAddress1(), address.getAddress2()));
  23. // 修改源对象的值
  24. user.getAddress().setAddress1("小区3");
  25. // false
  26. System.out.println(user == copyUser);
  27. // false
  28. System.out.println(user.getAddress().getAddress1() == copyUser.getAddress().getAddress1());
  29. // false
  30. System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1()));
  31. // true
  32. System.out.println(user.getAddress().getAddress2().equals(copyUser.getAddress().getAddress2()));
  33. }
  34. }
  35. package com.lyj.demo.pojo.cloneTest;
  36. import lombok.Getter;
  37. import lombok.Setter;
  38. /**
  39. * @author 凌兮
  40. * @date 2021/4/15 14:28
  41. */
  42. @Getter
  43. @Setter
  44. public class AddressConstruct {
  45. private String address1;
  46. private String address2;
  47. public AddressConstruct() {
  48. }
  49. public AddressConstruct(String address1, String address2) {
  50. this.address1 = address1;
  51. this.address2 = address2;
  52. }
  53. }

方式2:重载Clone()方法深拷贝

Object父类有个clone()的拷贝方法,不过它是protected类型的 ,我们需要重写它并修改为public类型,除此之外,子类还需要实现Cloneable接口来告诉JVM这个类上是可以拷贝的。

  1. package com.lyj.demo.pojo.cloneTest;
  2. import lombok.Getter;
  3. import lombok.Setter;
  4. /**
  5. * @author 凌兮
  6. * @date 2021/4/15 14:49
  7. *
  8. */
  9. @Setter
  10. @Getter
  11. public class AddressClone implements Cloneable{
  12. private String address1;
  13. private String address2;
  14. public AddressClone() {
  15. }
  16. public AddressClone(String address1, String address2) {
  17. this.address1 = address1;
  18. this.address2 = address2;
  19. }
  20. @Override
  21. protected AddressClone clone() throws CloneNotSupportedException {
  22. return (AddressClone) super.clone();
  23. }
  24. }
  25. package com.lyj.demo.pojo.cloneTest;
  26. import lombok.Getter;
  27. import lombok.Setter;
  28. /**
  29. * @author 凌兮
  30. * @date 2021/4/15 14:48
  31. * 通过实现Clone接口实现深拷贝
  32. */
  33. @Setter
  34. @Getter
  35. public class UserClone implements Cloneable{
  36. private String userName;
  37. private AddressClone address;
  38. public UserClone() {
  39. }
  40. public UserClone(String userName, AddressClone address) {
  41. this.userName = userName;
  42. this.address = address;
  43. }
  44. /**
  45. * Object父类有个clone()的拷贝方法,不过它是protected类型的,
  46. * 我们需要重写它并修改为public类型。除此之外,
  47. * 子类还需要实现Cloneable接口来告诉JVM这个类是可以拷贝的。
  48. * @return
  49. * @throws CloneNotSupportedException
  50. */
  51. @Override
  52. protected UserClone clone() throws CloneNotSupportedException {
  53. // 需要注意的是,super.clone()其实是浅拷贝,
  54. // 所以在重写UserClone类的clone()方法时,address对象需要调用address.clone()重新赋值
  55. UserClone userClone = (UserClone) super.clone();
  56. userClone.setAddress(this.address.clone());
  57. return userClone;
  58. }
  59. public static void main(String[] args) throws CloneNotSupportedException {
  60. AddressClone address = new AddressClone("小区1", "小区2");
  61. UserClone user = new UserClone("小李", address);
  62. UserClone copyUser = user.clone();
  63. user.getAddress().setAddress1("小区3");
  64. // false
  65. System.out.println(user == copyUser);
  66. // false
  67. System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1()));
  68. }
  69. }

需要注意的是,super.clone()其实是浅拷贝,所以在重写User类的clone()方法时,address对象需要调用address.clone()重新赋值。

方式3:Apache Commons Lang序列化方式深拷贝

Java提供了序列化的能力,我们可以先将源对象进行序列化,再反序列化生成拷贝对象。但是,使用序列化的前提是拷贝的类(包括其成员变量)需要实现Serializable接口。

Apache Commons Lang包对Java序列化进行了封装,我们可以直接使用它。

  1. package com.lyj.demo.pojo.cloneTest;
  2. import lombok.Getter;
  3. import lombok.Setter;
  4. import java.io.Serializable;
  5. /**
  6. * @author 凌兮
  7. * @date 2021/4/15 15:11
  8. */
  9. @Getter
  10. @Setter
  11. public class AddressSerializable implements Serializable {
  12. private String address1;
  13. private String address2;
  14. public AddressSerializable() {
  15. }
  16. public AddressSerializable(String address1, String address2) {
  17. this.address1 = address1;
  18. this.address2 = address2;
  19. }
  20. }
  21. package com.lyj.demo.pojo.cloneTest;
  22. import lombok.Getter;
  23. import lombok.Setter;
  24. import org.apache.commons.lang3.SerializationUtils;
  25. import java.io.Serializable;
  26. /**
  27. * @author 凌兮
  28. * @date 2021/4/15 15:10
  29. * 通过Apache Commons Lang 序列化方式深拷贝
  30. * Java提供了序列化的能力,我们可以先将源对象进行序列化,再反序列化生成拷贝对象。
  31. * 但是,使用序列化的前提是拷贝的类(包括其成员变量)需要实现Serializable接口。
  32. * Apache Commons Lang包对Java序列化进行了封装,我们可以直接使用它。
  33. */
  34. @Getter
  35. @Setter
  36. public class UserSerializable implements Serializable {
  37. private String userName;
  38. private AddressSerializable address;
  39. public UserSerializable() {
  40. }
  41. public UserSerializable(String userName, AddressSerializable address) {
  42. this.userName = userName;
  43. this.address = address;
  44. }
  45. public static void main(String[] args) {
  46. AddressSerializable address = new AddressSerializable("小区1", "小区2");
  47. UserSerializable user = new UserSerializable("小李", address);
  48. UserSerializable copyUser = SerializationUtils.clone(user);
  49. user.getAddress().setAddress1("小区3");
  50. // false
  51. System.out.println(user == copyUser);
  52. // false
  53. System.out.println(user.getAddress().getAddress1().equals(copyUser.getAddress().getAddress1()));
  54. }
  55. }

发表评论

表情:
评论列表 (有 0 条评论,70人围观)

还没有评论,来说两句吧...

相关阅读