Java的深拷贝和浅拷贝

首先我们来看一下Object类的clone()方法是怎么样做的:

package java1;

/**
 * @Author : hadoo
 * @Date : 2020/4/2 11:56
 */

public class Person implements Cloneable{

    //private Integer age;
    private int age;//阿里规范中规定pojo类中的属性强制使用包装类型,这里只是测试

    private String name;

    public Person(Integer age, String name) {
        super();
        this.age = age;
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return super.toString();
    }

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

    //对象拷贝
    private static void copyRealObject() throws CloneNotSupportedException{
        Person p = new Person(23, "zhang");
        Person p1 = (Person) p.clone();
        System.out.println(p);
        System.out.println(p1);
    }

    public static void main(String[] args) throws Exception{
        copyRealObject();
    }
}

image

从执行结果来看,使用clone()方法确确实实创建了一个新的对象,那么深拷贝和浅拷贝的区别是什么呢?

浅拷贝

我们对Peson类增加一个成员变量Son,同样为Person类型,在进行拷贝的过程中,基本数据类型没啥说的,只是进行了值得赋值,那么引用数据类型是怎么做的呢?

class Person{
    private int age;
    private String name;
    private Person son;
}
 private static void copyRealObject() throws CloneNotSupportedException{
        Person p = new Person(23, "zhang");
        Person son = new Person(10,"小张三");
        p.son = son;
        Person p1 = (Person) p.clone();
        System.out.println(p);
        System.out.println(p1);
        System.out.println(p.son == p1.son);	//true
    }

从执行结果来看,System.out.println(p.son == p1.son);返回的为true,说明在浅拷贝中,引用数据类型只是进行的引用的赋值,并没有复制实际的对象,p和p1两个引用变量中的son都是指向同一个son对象

深拷贝

从上面我们可以看到,Object类中默认的clone是浅拷贝,我们可以理解为只进行一层拷贝,那么怎么能把里面那一层对象也拷贝过来了?

我们假设有两个类,分别为Body、Head,Head为Body的子类,

static class Body implements Cloneable{
    public Head head;
    public Body() {}
    public Body(Head head) {this.head = head;}

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Body newBody =  (Body) super.clone();
        newBody.head = (Head) head.clone();
        return newBody;
    }

}
static class Head implements Cloneable{
    public  Face face;

    public Head() {}
    public Head(Face face){this.face = face;}
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
} 
public static void main(String[] args) throws CloneNotSupportedException {

    Body body = new Body(new Head());

    Body body1 = (Body) body.clone();

    System.out.println("body == body1 : " + (body == body1) );

    System.out.println("body.head == body1.head : " +  (body.head == body1.head));


}

我们可以使用这样的方式自己定义clone()方法,就可以将Body对象和Head对象同时进行拷贝

那么这么做存在什么问题呢?如果Head类中还组合了Face类,这也不是真正的深拷贝啊,只进行了两层拷贝,如果有三层怎么办?这种方法的另一个缺点就是引用链上的每一级对象都要被显式的拷贝,这样做实在麻烦,而且存在着很多问题,如果引用链中的一个对象来自第三方类库,并没有实现Clonable()接口,那么原始对象和拷贝出来的对象就不能完全独立。

除了这种方式以外,深拷贝还可以怎么做呢?

  • 序列化:序列化与反序列化,使用SerializationUtils的clone(Object obj)方法,要求拷贝的对象实现了Serializable,Map不行,使用HashMap即可
  • 用fastjson从Object转成json,然后转回object,本质上是反射:
 private Object deepCopyByJson(Object obj) {
     String json = JSON.toJSONString(obj);
     return JSON.parseObject(json, Object.class);
 }