• Clonable 接口 深拷贝与浅拷贝(超详细!!!代码附注释带图)


    Clonable又叫克隆接口,我们在使用Clonable之前,先来了解一下什么叫深拷贝,什么叫浅拷贝

    目录

    1.浅拷贝

    2.深拷贝

    3.Clonable接口


    1.浅拷贝

    定义:

    浅拷贝拷贝就是拷贝指向对象的指针,意思就是说:拷贝出来的目标对象的指针和源对象的指针指向的内存空间是同一块空间,浅拷贝只是一种简单的拷贝,让几个对象公用一个内存.

    这里我们用一张图配合代码来理解

    首先我们先写一段伪代码

    1. String a = new String();
    2. String b = a;

    在上述代码中,a和b指向的是同一块地址空间,当a中的内容发生改变的时候,b也会随之改变

    就像这个图一样,当对a中的数据进行操作的时候,b也会随之改变,我们称这种拷贝叫做浅拷贝

    2.深拷贝

    定义:

    深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响

     还是一样,先看代码

    1. String a = new String("abcdef");
    2. String b = new String();
    3. b += a;
    4. System.out.println(a);
    5. System.out.println(b);

    这里面就是给a和b同时开了两个空间,然后把a中的数值拷贝到b中去,这种当a中的值发生改变的时候,不会影响到b,就叫做深拷贝

    这张图中就比较好理解深拷贝了

    知道了深浅拷贝之后,我们再去认识Clonable接口

    3.Clonable接口

    Object 类中存在一个 clone 方法 , 调用这个方法可以创建一个对象的 " 拷贝 ". 但是要想合法调用 clone 方法 , 必须要 先实现 Clonable 接口 , 否则就会抛出 CloneNotSupportedException 异常 .
    下面我们一起看看这个接口
    1. //这里实现这个接口的作用是表明这个类可以被克隆,实际上这个接口里面什么都没有
    2. class Student implements Cloneable{
    3. public String name;
    4. public int Id;
    5. public String major;
    6. public Student(String name,int id,String major){
    7. this.name = name;
    8. this.Id = id;
    9. this.major = major;
    10. }
    11. @Override
    12. //这里重写的是Objec类中的clone方法
    13. //这里可以用try catch
    14. public Object clone() throws CloneNotSupportedException {
    15. Student o = null;
    16. //这里的super其实是再调用Object中的clone方法
    17. o = (Student) super.clone();
    18. return o;
    19. }
    20. }
    21. public class Main {
    22. public static void main(String[] args) throws CloneNotSupportedException {
    23. Student s1 = new Student("张三",123,"生物制药");
    24. Student s2 = (Student)s1.clone();
    25. System.out.println("s1 " + s1.name + s1.Id + s1.major);
    26. s1.Id = 5;
    27. System.out.println("s2 " + s2.name + s2.Id + s2.major);
    28. }
    29. }

     其实这个clone接口做的事情,就是在内存中克隆一份,传给s2

    借助图来理解

     这里再s1调用clone方法之后,便会克隆出来一个0X777并且返回给s2

    这样就做到深拷贝了吗?

    不一定哦,我们再看下面的代码

    1. class Student implements Cloneable{
    2. public String name;
    3. public int Id;
    4. public String major;
    5. public func A;
    6. public Student(String name,int id,String major,func A){
    7. this.name = name;
    8. this.Id = id;
    9. this.major = major;
    10. this.A = A;
    11. }
    12. @Override
    13. public Object clone() throws CloneNotSupportedException {
    14. Student o = null;
    15. o = (Student) super.clone();
    16. return o;
    17. }
    18. }
    19. //这里加了个类,让Student里面有个引用变量
    20. class func{
    21. int x = 10;
    22. }
    23. public class Main {
    24. public static void main(String[] args) throws CloneNotSupportedException {
    25. Student s1 = new Student("张三",123,"生物制药",new func());
    26. Student s2 = (Student) s1.clone();
    27. System.out.println("s1 " + s1.name + s1.Id + s1.major + s1.A.x);
    28. //这里是对s1进行的修改
    29. s1.A.x = 20;
    30. System.out.println("s2 " + s2.name + s2.Id + s2.major + s2.A.x);
    31. }
    32. }

    在上述代码中,加了一个func类,从而让Student有了引用变量,最后修改s1中的A的x

    上述代码的执行结果是

    最后修改s1中的A的x,导致s2中的A的x发生了改变,这种就不符合我们的预期了,成了浅拷贝了

    为什么会这样呢?

    一张图就能解释

    这里可以看到,s1中的func和s2中的func是一样的,都是一块地址,所以指向同一块空间,所以当s1中的func中的数据改变,s2中的数据也会随之改变

    那我们怎么做才能再让他进行深拷贝呢?

    看代码

    1. class Student implements Cloneable{
    2. public String name;
    3. public int Id;
    4. public String major;
    5. public func A;
    6. public Student(String name,int id,String major,func A){
    7. this.name = name;
    8. this.Id = id;
    9. this.major = major;
    10. this.A = A;
    11. }
    12. @Override
    13. public Object clone() throws CloneNotSupportedException {
    14. //这里先克隆出一个student,但是克隆完之后,func仍然指向的是同一个地址
    15. Student tmp =(Student) super.clone();
    16. //这里对func进行克隆,然后把克隆出来的结果复制给tmp.A
    17. tmp.A = (func)this.A.clone();
    18. //最后再进行返回,就能得到一个全新的深拷贝对象了
    19. return tmp;
    20. }
    21. }
    22. //因为上面的clone需要调用this.a.clone,所以这个func也必须实现克隆,并且重写克隆方法
    23. class func implements Cloneable{
    24. int x = 10;
    25. @Override
    26. protected Object clone() throws CloneNotSupportedException {
    27. return super.clone();
    28. }
    29. }
    30. public class Main {
    31. public static void main(String[] args) throws CloneNotSupportedException {
    32. Student s1 = new Student("张三",123,"生物制药",new func());
    33. Student s2 = (Student) s1.clone();
    34. System.out.println("s1 " + s1.name + s1.Id + s1.major + s1.A.x);
    35. s1.A.x = 20;
    36. System.out.println("s2 " + s2.name + s2.Id + s2.major + s2.A.x);
    37. }
    38. }

    代码中有详细解析

    运行结果为

    所以,这样我们就做到了深拷贝,但是问题又来了,如果func中又有一个引用类型呢?

    所以所谓的深拷贝,浅拷贝,需要根据业务需求进行操作,并没有绝对的深拷贝 

    温馨提示:当你想要复制一个集合类的对象的时候,比如队列,栈之类的,不妨试一下clone

  • 相关阅读:
    【react】手把手学习react - 元素条件渲染
    2022年湖南省成人高考考试成绩查询渠道
    WPF 入门教程数据绑定(一)
    docker部署springboot项目(更新无需重新build)
    Cloud整合Zookeeper代替Eureka
    路径总和III——对前缀和解法的解释
    漏洞(某渗透测试)修复-springboot项目使用内置tomcat去除\隐藏页面的异常报错信息以及版本号信息,亲测有效。
    如何在Python中实现安全的密码存储与验证
    【精讲】mustache表达式写法、vue常用指令、v-bind多种绑定事件、技能提升
    自学的程序员一点竞争力都没有吗?投了5天简历,一个面试通知都没有怎么办?
  • 原文地址:https://blog.csdn.net/qq_55546526/article/details/126669540