美文网首页Java
关于Java的浅拷贝和深拷贝

关于Java的浅拷贝和深拷贝

作者: 西敏寺钟声 | 来源:发表于2020-07-23 23:20 被阅读0次

    浅拷贝和深拷贝是什么?

    浅拷贝和深拷贝都是针对已经存在了的对象的操作,在java中,基本数据类型有八种,和引用数据类型。在程序中,一般用 = 来做赋值的操作,对于基本数据类型,实际上是拷贝它的值,而对于引用数据类型,则是拷贝的它的引用地址,举例如下:

    @Test
    public void m2() {
        int i = 1;
        int j = i;
        System.out.println(j);
    
        Student stu1 = new Student("zhangsan", 12);
        System.out.println(stu1);
        Student stu2 = stu1;
        stu2.setAge(20);
        System.out.println(stu1);
        System.out.println(stu2);
    }
    

    运行结果:

    1
    Student{name='zhangsan', age=12, active=null}
    Student{name='zhangsan', age=20, active=null}
    Student{name='zhangsan', age=20, active=null}
    

    从运行的结果,发现一个问题,int类型的值不用多说,而在引用类型中,明明修改的是stu2的age,为什么stu1的age也发生了变化,这里就涉及到了值传递和引用传递的问题了,ok,后面介绍。

    Java中的clone()方法

    说到拷贝操作,那java必定提供了API来供我们使用,那就是Object类中的clone()方法了(它是一个native方法),既然是超类中的protected方法,那么子类中就有必要重写一下,所有使用调用clone()来拷贝的时候,其对象必须要实现标识接口Cloneable,否则就会抛出CloneNotSupportedException这个异常。

    浅拷贝

    首先创建一个Studen类,并实现Cloneable接口,重写clone方法。

    public class Student implements Cloneable {
    
        private String name;
    
        private int age;
    
        private Active active;
    
        public Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
        // 省略get、set方法
    
    }
    

    Active类的实现:

    public class Active implements Cloneable {
    
        private String name;
    
        public Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
        public Active(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    测试用例:用clone方法,复制新new出来的student1,得到student2,再做各种比较。

    @Test
    public void m1() throws Exception {
        Student student1 = new Student();
        student1.setAge(10);
        student1.setName("zhangsan");
        student1.setActive(new Active("pingpang"));
        // 原始对象的hashcode
        System.out.println(student1.hashCode());
        // 克隆出来的对象
        Student student2 = (Student) student1.clone();
        // 克隆出来的对象的hashcode
        System.out.println(student2.hashCode());
        // 两个对象是否一样
        System.out.println(student1 == student2);
        // 两个对象比较
        System.out.println(student1);
        System.out.println(student2);
        // 两个对象各自的引用比较
        System.out.println(student1.getActive());
        System.out.println(student2.getActive());
    }
    

    运行结果:

    697960108
    943010986
    false
    Student@299a06ac
    Student@383534aa
    Active@6bc168e5
    Active@6bc168e5
    

    可以得出关于浅拷贝的几点结论:

    • clone()方法,会创建一个新的对象。
    • 浅拷贝基本数据类型是拷贝值。
    • 拷贝引用数据类型如:Active ,则拷贝的是它的引用地址。

    深拷贝

    Student类,在克隆此类对象的时候,将此对象中的引用属性也克隆一份,只有clone方法不同:

    public class Student implements Cloneable {
    
        private String name;
    
        private int age;
    
        private Active active;
    
        public Object clone() throws CloneNotSupportedException {
            Student student = (Student) super.clone();
            student.active = (Active) this.active.clone();
            return student;
        }
    
        // get、set方法省略
    
    }
    

    Active类,实现Cloneable接口,并重写了clone方法:

    public class Active implements Cloneable {
    
        private String name;
    
        public Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
        public Active(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    测试用例:

    @Test
    public void m1() throws Exception {
        Student student1 = new Student();
        student1.setAge(10);
        student1.setName("zhangsan");
        student1.setActive(new Active("pingpang"));
        // 原始对象的hashcode
        System.out.println(student1.hashCode());
        // 克隆出来的对象
        Student student2 = (Student) student1.clone();
        // 克隆出来的对象的hashcode
        System.out.println(student2.hashCode());
        // 两个对象是否一样
        System.out.println(student1 == student2);
        // 两个对象比较
        System.out.println(student1);
        System.out.println(student2);
        // 两个对象各自的引用比较
        System.out.println(student1.getActive());
        System.out.println(student2.getActive());
    }
    

    测试结果中显示不同student对象中的active对象也不同了:

    697960108
    943010986
    false
    com.nmys.story.interview.copy_interview.Student@299a06ac
    com.nmys.story.interview.copy_interview.Student@383534aa
    com.nmys.story.interview.copy_interview.Active@6bc168e5
    com.nmys.story.interview.copy_interview.Active@7b3300e5
    

    可以得出深拷贝的几点结论:

    • 深拷贝是完全创建一个新的对象
    • 深拷贝也会将原始对象中的引用对象重新复制一份

    总结

    1. 一定要实现Cloneable接口。
    2. 重写clone()方法,注意:默认是浅拷贝,这里需要将引用类型进行深拷贝处。
    3. 特殊:String类虽然是引用类型,但是是final类,同时也有字符串常量池的存在,不必进行处理。
    4. 深拷贝可以通过上面所说的clone方法实现,还可以通过序列化来实现。
    5. 当时写测试用例的时候,我这里用了lombok的注解@Data,为的就是不用手写get、set方法了,但测试的时候,发现clone出来的对象的hashcode是一样的,原因是lombok在为我们生成get、set的同时,toString、hashCode、equals等方法都替我们重写了,导致clone出来的类虽然 ==false ,但是hashCode却一样,建议还是手动写比较好。
    6. 最后再总结下,假设,A 类中引用了 B 类,B 类中引用了 C 类,对象分别对应 a,b,c,浅拷贝 a 的结果 a1,b,c。深拷贝 a 的结果 a1,b1,c1。

    相关文章

      网友评论

        本文标题:关于Java的浅拷贝和深拷贝

        本文链接:https://www.haomeiwen.com/subject/rzugkktx.html