• 深拷贝代码实现

    深度遍历式拷贝

    虽然clone()方法可以完成对象的拷贝工作,但是注意:clone()方法默认是浅拷贝行为,就像上面的例子一样。若想实现深拷贝需覆写 clone()方法实现引用对象的深度遍历式拷贝,进行地毯式搜索。

    所以对于上面的例子,如果想实现深拷贝,首先需要对更深一层次的引用类Major做改造,让其也实现Cloneable接口并重写clone()方法:

    public class Major implements Cloneable {

        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
        
        // ... 其他省略 ...
    }

    其次我们还需要在顶层的调用类中重写clone方法,来调用引用类型字段的clone()方法实现深度拷贝,对应到本文那就是Student类:

    public class Student implements Cloneable {

        @Override
        public Object clone() throws CloneNotSupportedException {
            Student student = (Student) super.clone();
            student.major = (Major) major.clone(); // 重要!!!
            return student;
        }
        
        // ... 其他省略 ...
    }

    这时候上面的测试用例不变,运行可得结果:

    很明显,这时候student1student2两个对象就完全独立了,不受互相的干扰。

    利用反序列化实现深拷贝

    记得在前文《序列化/反序列化,我忍你很久了》中就已经详细梳理和总结了「序列化和反序列化」这个知识点了。

    利用反序列化技术,我们也可以从一个对象深拷贝出另一个复制对象,而且这货在解决多层套娃式的深拷贝问题时效果出奇的好。

    所以我们这里改造一下Student类,让其clone()方法通过序列化和反序列化的方式来生成一个原对象的深拷贝副本:

    public class Student implements Serializable {

        private String name;  // 姓名
        private int age;      // 年龄
        private Major major;  // 所学专业

        public Student clone() {
            try {
                // 将对象本身序列化到字节流
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                ObjectOutputStream objectOutputStream =
                        new ObjectOutputStream( byteArrayOutputStream );
                objectOutputStream.writeObject( this );

                // 再将字节流通过反序列化方式得到对象副本
                ObjectInputStream objectInputStream =
                        new ObjectInputStream( new ByteArrayInputStream( byteArrayOutputStream.toByteArray() ) );
                return (Student) objectInputStream.readObject();

            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }

            return null;
        }
        
        // ... 其他省略 ...
    }

    当然这种情况下要求被引用的子类(比如这里的Major类)也必须是可以序列化的,即实现了Serializable接口:

    public class Major implements Serializable {
      
      // ... 其他省略 ...
        
    }

    这时候测试用例完全不变,直接运行,也可以得到如下结果:

    很明显,这时候student1student2两个对象也是完全独立的,不受互相的干扰,深拷贝完成。


    后 记

    好了,关于「深拷贝」和「浅拷贝」这个问题这次就聊到这里吧。本以为这篇会很快写完,结果又扯出了这么多东西,不过这样一梳理、一串联,感觉还是清晰了不少。


    —————END—————



    喜欢本文的朋友,欢迎关注公众号 程序员小灰,收看更多精彩内容

    一个工作三年的同事,居然还搞不清深拷贝、浅拷贝...-LMLPHP


    本文分享自微信公众号 - 程序员小灰(chengxuyuanxiaohui)。
    如有侵权,请联系 [email protected] 删除。
    本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

    09-03 09:16