如何实现对象克隆与深拷贝?

1、实现 Cloneable 接口,重写 clone() 方法。

2、不实现 Cloneable 接口,会报 CloneNotSupportedException 异常。

  1. package constxiong.interview;
  2. /**
  3. * 测试克隆
  4. * @author ConstXiong
  5. * @date 2019-06-18 11:21:21
  6. */
  7. public class TestClone {
  8. public static void main(String[] args) throws CloneNotSupportedException {
  9. Person p1 = new Person(1, "ConstXiong");//创建对象 Person p1
  10. Person p2 = (Person)p1.clone();//克隆对象 p1
  11. p2.setName("其不答");//修改 p2的name属性,p1的name未变
  12. System.out.println(p1);
  13. System.out.println(p2);
  14. }
  15. }
  16. /**
  17. *
  18. * @author ConstXiong
  19. * @date 2019-06-18 11:54:35
  20. */
  21. class Person implements Cloneable {
  22. private int pid;
  23. private String name;
  24. public Person(int pid, String name) {
  25. this.pid = pid;
  26. this.name = name;
  27. System.out.println("Person constructor call");
  28. }
  29. public int getPid() {
  30. return pid;
  31. }
  32. public void setPid(int pid) {
  33. this.pid = pid;
  34. }
  35. public String getName() {
  36. return name;
  37. }
  38. public void setName(String name) {
  39. this.name = name;
  40. }
  41. @Override
  42. protected Object clone() throws CloneNotSupportedException {
  43. return super.clone();
  44. }
  45. @Override
  46. public String toString() {
  47. return "Person [pid:"+pid+", name:"+name+"]";
  48. }
  49. }

打印结果

  1. Person constructor call
  2. Person [pid:1, name:ConstXiong]
  3. Person [pid:1, name:其不答]

 

3、Object 的 clone() 方法是浅拷贝,即如果类中属性有自定义引用类型,只拷贝引用,不拷贝引用指向的对象。

 

可以使用下面的两种方法,完成 Person 对象的深拷贝。

方法1、对象的属性的Class 也实现 Cloneable 接口,在克隆对象时也手动克隆属性。

  1. @Override
  2. public Object clone() throws CloneNotSupportedException {
  3. DPerson p = (DPerson)super.clone();
  4. p.setFood((DFood)p.getFood().clone());
  5. return p;
  6. }

 

完整代码

  1. package constxiong.interview;
  2. /**
  3. * 测试克隆
  4. * @author ConstXiong
  5. * @date 2019-06-18 11:21:21
  6. */
  7. public class TestManalDeepClone {
  8. public static void main(String[] args) throws Exception {
  9. DPerson p1 = new DPerson(1, "ConstXiong", new DFood("米饭"));//创建Person 对象 p1
  10. DPerson p2 = (DPerson)p1.clone();//克隆p1
  11. p2.setName("其不答");//修改p2的name属性
  12. p2.getFood().setName("面条");//修改p2的自定义引用类型 food 属性
  13. System.out.println(p1);//修改p2的自定义引用类型 food 属性被改变,p1的自定义引用类型 food 属性也随之改变,说明p2的food属性,只拷贝了引用,没有拷贝food对象
  14. System.out.println(p2);
  15. }
  16. }
  17. class DPerson implements Cloneable {
  18. private int pid;
  19. private String name;
  20. private DFood food;
  21. public DPerson(int pid, String name, DFood food) {
  22. this.pid = pid;
  23. this.name = name;
  24. this.food = food;
  25. System.out.println("Person constructor call");
  26. }
  27. public int getPid() {
  28. return pid;
  29. }
  30. public void setPid(int pid) {
  31. this.pid = pid;
  32. }
  33. public String getName() {
  34. return name;
  35. }
  36. public void setName(String name) {
  37. this.name = name;
  38. }
  39. @Override
  40. public Object clone() throws CloneNotSupportedException {
  41. DPerson p = (DPerson)super.clone();
  42. p.setFood((DFood)p.getFood().clone());
  43. return p;
  44. }
  45. @Override
  46. public String toString() {
  47. return "Person [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
  48. }
  49. public DFood getFood() {
  50. return food;
  51. }
  52. public void setFood(DFood food) {
  53. this.food = food;
  54. }
  55. }
  56. class DFood implements Cloneable{
  57. private String name;
  58. public DFood(String name) {
  59. this.name = name;
  60. }
  61. public String getName() {
  62. return name;
  63. }
  64. public void setName(String name) {
  65. this.name = name;
  66. }
  67. @Override
  68. public Object clone() throws CloneNotSupportedException {
  69. return super.clone();
  70. }
  71. }

 

打印结果

  1. Person constructor call
  2. Person [pid:1, name:ConstXiong, food:米饭]
  3. Person [pid:1, name:其不答, food:面条]

 

方法2、结合序列化(JDK java.io.Serializable 接口、JSON格式、XML格式等),完成深拷贝

结合 java.io.Serializable 接口,完成深拷贝

  1. package constxiong.interview;
  2. import java.io.ByteArrayInputStream;
  3. import java.io.ByteArrayOutputStream;
  4. import java.io.IOException;
  5. import java.io.ObjectInputStream;
  6. import java.io.ObjectOutputStream;
  7. import java.io.Serializable;
  8. public class TestSeriazableClone {
  9. public static void main(String[] args) {
  10. SPerson p1 = new SPerson(1, "ConstXiong", new SFood("米饭"));//创建 SPerson 对象 p1
  11. SPerson p2 = (SPerson)p1.cloneBySerializable();//克隆 p1
  12. p2.setName("其不答");//修改 p2 的 name 属性
  13. p2.getFood().setName("面条");//修改 p2 的自定义引用类型 food 属性
  14. System.out.println(p1);//修改 p2 的自定义引用类型 food 属性被改变,p1的自定义引用类型 food 属性未随之改变,说明p2的food属性,只拷贝了引用和 food 对象
  15. System.out.println(p2);
  16. }
  17. }
  18. class SPerson implements Cloneable, Serializable {
  19. private static final long serialVersionUID = -7710144514831611031L;
  20. private int pid;
  21. private String name;
  22. private SFood food;
  23. public SPerson(int pid, String name, SFood food) {
  24. this.pid = pid;
  25. this.name = name;
  26. this.food = food;
  27. System.out.println("Person constructor call");
  28. }
  29. public int getPid() {
  30. return pid;
  31. }
  32. public void setPid(int pid) {
  33. this.pid = pid;
  34. }
  35. public String getName() {
  36. return name;
  37. }
  38. public void setName(String name) {
  39. this.name = name;
  40. }
  41. /**
  42. * 通过序列化完成克隆
  43. * @return
  44. */
  45. public Object cloneBySerializable() {
  46. Object obj = null;
  47. try {
  48. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  49. ObjectOutputStream oos = new ObjectOutputStream(baos);
  50. oos.writeObject(this);
  51. ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
  52. ObjectInputStream ois = new ObjectInputStream(bais);
  53. obj = ois.readObject();
  54. } catch (ClassNotFoundException e) {
  55. e.printStackTrace();
  56. } catch (IOException e) {
  57. e.printStackTrace();
  58. }
  59. return obj;
  60. }
  61. @Override
  62. public String toString() {
  63. return "Person [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
  64. }
  65. public SFood getFood() {
  66. return food;
  67. }
  68. public void setFood(SFood food) {
  69. this.food = food;
  70. }
  71. }
  72. class SFood implements Serializable {
  73. private static final long serialVersionUID = -3443815804346831432L;
  74. private String name;
  75. public SFood(String name) {
  76. this.name = name;
  77. }
  78. public String getName() {
  79. return name;
  80. }
  81. public void setName(String name) {
  82. this.name = name;
  83. }
  84. }

 

打印结果

  1. Person constructor call
  2. Person [pid:1, name:ConstXiong, food:米饭]
  3. Person [pid:1, name:其不答, food:面条]

 

点点赞赏,手留余香

给TA打赏
共0人
还没有人赞赏,快来当第一个赞赏的人吧!
    Java

    深拷贝和浅拷贝区别是什么?

    2020-7-23 13:54:45

    Java

    Java跨平台运行的原理

    2020-7-23 14:03:25

    本站所发布的一切源码、模板、应用等文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权。本站内容适用于DMCA政策。若您的权利被侵害,请与我们联系处理,站长 QQ: 84087680 或 点击右侧 私信:盾给网 反馈,我们将尽快处理。
    ⚠️
    本站所发布的一切源码、模板、应用等文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权。本站内容适用于DMCA政策
    若您的权利被侵害,请与我们联系处理,站长 QQ: 84087680 或 点击右侧 私信:盾给网 反馈,我们将尽快处理。
    0 条回复 A文章作者 M管理员
    欢迎您,新朋友,感谢参与互动!
      暂无讨论,说说你的看法吧
    个人中心
    购物车
    优惠劵
    今日签到
    私信列表
    搜索