高质量编码--使用序列化实现对象的拷贝

#回顾
回顾下对象拷贝的知识,为什么要拷贝对象呢,因为new对象往往是消耗资源的,使用对象拷贝的方式可以节省创建对象时的资源消耗,尤其对于大对象而言

我们可以看下简单实现,下面这个类实现了Cloneable接口

public class Person implements Cloneable{
        private String name;
        private Person parent;

        Person(String name){
            this.name = name;
        }

        Person(String name,Person parent){
            this.name = name;
            this.parent = parent;
        }

        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }

        // get,set方法省略...
}

类有了,下面这个方法会输出什么呢?

public class Main {

    public static void main(String[] args) throws CloneNotSupportedException {
        Person father = new Person("老爹");
        Person xiaoming = new Person("小明", father);
        Person xiaogang = xiaoming.clone();
        xiaogang.name = "小刚";
        xiaoming.parent.name = "干爹";
        System.out.printf("%s的父亲-%s\n",xiaoming.name,xiaoming.parent.name);
        System.out.printf("%s的父亲-%s\n",xiaogang.name,xiaogang.parent.name);
    }

Look

我们现在假设个场景,小明和小刚是兄弟,他们俩的老爹欠了隔壁王大爷5块钱,木钱还咋办,老王说你让一个儿子认我做干爹,这事算结了,老爹没办法啊,只好同意了,于是小明认了老王做干爹,小刚看见了也去认老王当干爹去了,得,两个儿子全没了,老爹要气死

这里就涉及到了浅拷贝的知识

#浅拷贝
调用clone方法只会复制对象的基本类型和String,而对象则是复制引用,即小明和小刚的引用都是指向同一个父亲。
有没有办法给小刚复制一个父亲,这样小明修改父亲属性就不会影响到小刚了。

#重写clone方法
其实是有办法的,代码如下,给小刚重新new一个父亲就行。

protected Person clone() throws CloneNotSupportedException {
    Person person = super.clone();
    person.setPerson(new Person(person.getName));
    return person;
}

#序列化
我们考虑这样一个问题,每次对象复制都要重写clone对象是不是很费事
因此我们想到了其他方法,我们可以通过序列化的方式来处理,在内存中通过字节流的拷贝来实现。
也就是把小明写到一个字节流中,再从字节流中读出来,这样就可以重建一个新对象了,这个对象跟小明不存在引用共享的问题,相当于深拷贝一个对象。

代码如下

class CloneUtils{
    // 拷贝一个对象
    public static <T extends Serializable> T clone(T obj){
        // 拷贝产生的对象
        T clonedObj = null;
        try {
            // 读取对象字节数据
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);
            oos.close();
            // 分配内存空间,写入原始对象,生成新对象
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            // 返回新对象,进行类型转换
            clonedObj = (T) ois.readObject();
            ois.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return clonedObj;
    }
}

当然,传入的类要实现Serializable.

今天的文章结束了~
Best Wish to you

posted on 2020-03-28 17:31  布衣少年  阅读(121)  评论(0编辑  收藏  举报

导航