关于深拷贝和浅拷贝

本文详细介绍了Java中对象拷贝的三种方式:直接赋值、浅拷贝和深拷贝。通过实例演示了不同拷贝方式下对象内存地址的变化,对比了深拷贝的两种实现方法:实现Cloneable接口与序列化。
场景

现在有一个Student对象,想要将这个对象拷贝一份,有三种拷贝方式:
1.直接赋值给另一个变量,这样内存地址完全没变,没有实现拷贝

Student student = new Student();
Student student1 = student;

2.浅拷贝(省略其余代码),只对Student 对象实现Cloneable接口,并实现clone方法。这样的结果是,Student对象实现了拷贝,但是Student内的其他对象,比如Student内部有一个成员Person对象就没有实现拷贝。

3.深拷贝,就是我们通常想要的完全拷贝,不仅该对象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内存地址为:copy.Student@1540e19d
         * studentCopy1内存地址为:copy.Student@1540e19d
         * 结论:内存地址一致
         * */
        Student studentCopy1 = student;
        System.out.println("student内存地址为:"+ student);
        System.out.println("studentCopy1内存地址为:"+ studentCopy1);

2.浅拷贝

        /*3. 浅拷贝测试*/
        Student studentCopy2 = student.clone();
        /*
        * 内存地址测试
        * 打印结果:
        * student内存地址为:copy.Student@1540e19d
        * studentCopy2内存地址为:copy.Student@677327b6
        * student中Person内存地址为:copy.Person@14ae5a5
        * studentCopy2中Person内存地址为:copy.Person@14ae5a5
        * 结论:浅拷贝对于对象中的对象是拷贝不了的
        * */
        System.out.println("student内存地址为:"+ student);
        System.out.println("studentCopy2内存地址为:"+ studentCopy2);
        System.out.println("student中Person内存地址为:"+ student.getPerson());
        System.out.println("studentCopy2中Person内存地址为:"+ studentCopy2.getPerson());

3.深拷贝

        /*4. 深拷贝测试*/
        long start = System.currentTimeMillis();
        Student studentCopy3 = student.clone();
        long end = System.currentTimeMillis();
        /*
         * 内存地址测试
         * 打印结果:
         * student内存地址为:copy.Student@1540e19d
         * studentCopy3内存地址为:copy.Student@6d6f6e28
         * student中Person内存地址为:copy.Person@14ae5a5
         * studentCopy3中Person内存地址为:copy.Person@135fbaa4
         * 耗时:0ms
         * 结论:深拷贝确实可以做到所有对象拷贝,但是太麻烦,但性能极高
         * */
        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");

4.序列化实现深拷贝

        /*5. 序列化实现深拷贝测试*/
        start = System.currentTimeMillis();
        Student studentCopy4 = clone(student); //序列化方法
        end = System.currentTimeMillis();
        /*
         * 内存地址测试
         * 打印结果:
         * student内存地址为:copy.Student@1540e19d
         * studentCopy4内存地址为:copy.Student@1d81eb93
         * student中Person内存地址为:copy.Person@14ae5a5
         * studentCopy4中Person内存地址为:copy.Person@7291c18f
         * 耗时:85ms
         * 结论:序列化实现深拷贝很方便,但是性能相比较前者慢很多
         * */
        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");

序列化实现的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
参考

Java 深拷贝和浅拷贝

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值