您好,欢迎来到花图问答。
搜索
您的当前位置:首页关于深拷贝和浅拷贝

关于深拷贝和浅拷贝

来源:花图问答

场景

现在有一个Student对象,想要将这个对象拷贝一份,有三种拷贝方式:

  1. 直接赋值给另一个变量,这样内存地址完全没变,没有实现拷贝
Student student = new Student();
Student student1 = student;
  1. 浅拷贝(省略其余代码),只对Student 对象实现Cloneable接口,并实现clone方法。这样的结果是,Student对象实现了拷贝,但是Student内的其他对象,比如Student内部有一个成员Person对象就没有实现拷贝。

  2. 深拷贝,就是我们通常想要的完全拷贝,不仅该对象Student实现拷贝,该对象内部Person对象同样也实现了拷贝。深拷贝通常有两种方法,一种一层一层的实现Cloneable接口,还有一种是序列化方法。两者相比较,前者繁琐,但效率高,后者简便但效率低。

具体实现+测试

两个对象代码(省略无关get/set方法):
Person对象

public class Person implements Cloneable, Serializable {

    private String name;
    private int age;
    private Integer age1;
    
    @Override
    protected Person clone() {
        try {
            return (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }
}

Student对象

public class Student implements Cloneable, Serializable {
    private Person person;
    private String className;

    @Override
    protected Student clone() {
        try {
            Student student = (Student) super.clone();
            student.setPerson(this.getPerson().clone());
            return student;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }
}

  1. 直接赋值
        /*1. 初始化一些值*/
        Person person = new Person("张三", 20, 21);
        Student student = new Student(person, "三年二班");

        /*2. 直接拷贝的情况*/
        Student studentCopy1 = student;
        System.out.println("student内存地址为:"+ student);
        System.out.println("studentCopy1内存地址为:"+ studentCopy1);

直接拷贝打印结果为:
/*
* 内存地址测试
* 打印结果为:
* student内存地址为:copy.Student@1540e19d
* studentCopy1内存地址为:copy.Student@1540e19d(内存一致)
* 结论:内存地址一致,未起到拷贝作用
* */

  1. 浅拷贝
        /*3. 浅拷贝测试*/
        Student studentCopy2 = student.clone();
        System.out.println("student内存地址为:"+ student);
        System.out.println("studentCopy2内存地址为:"+ studentCopy2);
        System.out.println("student中Person内存地址为:"+ student.getPerson());
        System.out.println("studentCopy2中Person内存地址为:"+ studentCopy2.getPerson());

浅拷贝打印结果为:
/*
* 内存地址测试
* 打印结果:
* student内存地址为:copy.Student@1540e19d
* studentCopy2内存地址为:copy.Student@677327b6(内存不一致)
* student中Person内存地址为:copy.Person@14ae5a5
* studentCopy2中Person内存地址为:copy.Person@14ae5a5(内存一致)
* 结论:浅拷贝对于对象中的对象是拷贝不了的
* */

  1. 深拷贝
        /*4. 深拷贝测试*/
        long start = System.currentTimeMillis();
        Student studentCopy3 = student.clone();
        long end = System.currentTimeMillis();
        System.out.println("student内存地址为:"+ student);
        System.out.println("studentCopy3内存地址为:"+ studentCopy3);
        System.out.println("student中Person内存地址为:"+ student.getPerson());
        System.out.println("studentCopy3中Person内存地址为:"+ studentCopy3.getPerson());
        System.out.println("耗时:" + (end - start) + "ms");

深拷贝打印结果为:
/*
* 内存地址测试
* 打印结果:
* student内存地址为:copy.Student@1540e19d
* studentCopy3内存地址为:copy.Student@6d6f6e28(内存不一致)
* student中Person内存地址为:copy.Person@14ae5a5
* studentCopy3中Person内存地址为:copy.Person@135fbaa4 (内存不一致)
* 耗时:0ms
* 结论:深拷贝确实可以做到所有对象拷贝,操作较为麻烦,但性能极高
* */

  1. 序列化实现深拷贝
        /*5. 序列化实现深拷贝测试*/
        start = System.currentTimeMillis();
        Student studentCopy4 = clone(student); //序列化方法
        end = System.currentTimeMillis();
        System.out.println("student内存地址为:"+ student);
        System.out.println("studentCopy4内存地址为:"+ studentCopy4);
        System.out.println("student中Person内存地址为:"+ student.getPerson());
        System.out.println("studentCopy4中Person内存地址为:"+ studentCopy4.getPerson());
        System.out.println("耗时:" + (end - start) + "ms");

序列化实现深拷贝打印结果为:
/*
* 内存地址测试
* 打印结果:
* student内存地址为:copy.Student@1540e19d
* studentCopy4内存地址为:copy.Student@1d81eb93(内存不一致)
* student中Person内存地址为:copy.Person@14ae5a5
* studentCopy4中Person内存地址为:copy.Person@7291c18f(内存不一致)
* 耗时:85ms
* 结论:序列化实现深拷贝很方便,但是性能相比较前者慢很多
* */

序列化具体实现的方法,即下面的clone方法,具体代码为:

    public static <T extends Serializable> T clone(T obj){
        T cloneObj = null;
        try {
            //写入字节流
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            ObjectOutputStream obs = new ObjectOutputStream(out);
            obs.writeObject(obj);
            obs.close();

            //分配内存,写入原始对象,生成新对象
            ByteArrayInputStream ios = new ByteArrayInputStream(out.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(ios);
            //返回生成的新对象
            cloneObj = (T) ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cloneObj;
    }

结论

浅拷贝往往达不到我们要的效果,深拷贝才可以。
深拷贝有两种方式,无所谓耗时的采用实现Cloneable接口方式,考虑效率的采用序列化方式。

END

参考

Copyright © 2019- huatuowenda.com 版权所有 湘ICP备2023022495号-1

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务