【语言学习】JAVA 深度拷贝 - hippowc/hippowc.github.io GitHub Wiki

使用场景

这次使用到java的深度拷贝,主要是用于多线程场景。当一个多线程任务传入的参数是复杂对象时,需要控制其他线程对这个对象的修改不能影响到其他线程的使用,这个时候考虑为线程传递的参数是原参数的一份拷贝。

这个思路借鉴了ThreadLocal的实现,但实际ThreadLocal中的参数并没有实现深度copy,对不同线程的对象如果进行修改,也是会影响到其他线程的使用。

深度拷贝

深拷贝和浅拷贝都是针对于复杂类型的。

对于对象或数组类型,当我们将a赋值给b,然后更改b中的属性,a也会随着变化。也就是说a和b指向了同一块内存,所以修改其中任意的值,另一个值都会随之变化,拷贝的仅仅是引用,这就是浅拷贝。

相应的,如果给b放到新的内存中,将a的各个属性都复制到新内存里,就是深拷贝。当b中的属性有变化的时候,a内的属性不会发生变化。

如何实现深度copy

实现clonable接口

在Object类中实现了clone方法,不过是protected方法,只能在子类中进行调用。

那么我们先不实现clonable接口,实现一个类的自己的方式,调用Object的clone方法:

public Foo myClone() {
        Foo newFoo = null;
        try {
            newFoo = (Foo)super.clone();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }

        return newFoo;
    }

是会抛出异常的

需要实现clonable接口:但是这个接口也没有定义任何方法,就是一个标记接口,实现该接口后就可以使用Object的clone方法了。

但是也是浅拷贝,就是说如果这个对象中的某个属性值,复杂对象(譬如数组)的话,那么clone的对象中这个属性指向的是同一个。所以对于属性中的复杂对象都要实现Clonable接口。

通过序列化

序列化的思路就是先将对象序列化到内存中,在从内存中反序列化出一个对象,序列化的前提也是需要对象先实现一个序列化的接口 Serializable,否则在序列化对象的时候会报错

// 写入字节流,即写入内存
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// 负责转换写入的对象
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(foo);
oos.close();

// 从字节中读数据
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
// 负责字节转换成对象
ObjectInputStream ois = new ObjectInputStream(bais);
copy = (Foo)ois.readObject();

这种序列化的方式也是各种rpc实现的基础