Java 浅拷贝和深拷贝

46 阅读2分钟

浅拷贝

浅拷贝会创建一个新的对象,复制该对象的基本类型引用,但不复制该对象内的其他对象引用。一个对象要支持浅拷贝,需要实现Cloneable接口,不然在调用clone()方法时会抛出异常

    protected Object clone() throws CloneNotSupportedException {
        if (!(this instanceof Cloneable)) {
            throw new CloneNotSupportedException("Class " + getClass().getName() +
                                                 " doesn't implement Cloneable");
        }

        return internalClone();
    }

看个简单的demo

public class ShallowCopy implements Cloneable {
    int i = 10;
    String str = "ShallowCopy";
    Shallow shallow = new Shallow();

    public static class Shallow {
        String shallow = "shallow";
    }

    public static void testShallowCopy() {
        ShallowCopy shallowCopy = new ShallowCopy();
        try {
            ShallowCopy shallowCopyClone = (ShallowCopy) shallowCopy.clone();
            LogUtil.info("ShallowCopy", "(shallowCopy == shallowCopyClone) = " + (shallowCopy == shallowCopyClone));
            LogUtil.info("ShallowCopy", "(shallowCopy.equals(shallowCopyClone)) = " + (shallowCopy.equals(shallowCopyClone)));
            LogUtil.info("ShallowCopy", "(shallowCopy.shallow == shallowCopyClone.shallow) = " + (shallowCopy.shallow == shallowCopyClone.shallow));
            LogUtil.info("ShallowCopy", "(shallowCopy.shallow.equals(shallowCopyClone.shallow)) = " + (shallowCopy.shallow.equals(shallowCopyClone.shallow)));
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

打印结果如下

2025-03-13 16:53:25.564   (shallowCopy == shallowCopyClone) = false
2025-03-13 16:53:25.564   (shallowCopy.equals(shallowCopyClone)) = false
2025-03-13 16:53:25.564   (shallowCopy.shallow == shallowCopyClone.shallow) = true
2025-03-13 16:53:25.564   (shallowCopy.shallow.equals(shallowCopyClone.shallow)) = true

我们发现shallowCopyshallowCopyClone指向不同的对象,但是里面的shallow却是同一个。如果要实现两个shallowCopyshallowCopyClone两个完全不同的对象,也即是要实现深拷贝,有两个办法,下文介绍两种方法

深拷贝

  1. 内部的对象引用所属类也继承clone接口,重写clone()方法,例如
public class ShallowCopy implements Cloneable {
    int i = 10;
    String str = "ShallowCopy";
    Shallow shallow = new Shallow();

    @NonNull
    @Override
    protected Object clone() throws CloneNotSupportedException {
        ShallowCopy cloned = (ShallowCopy)super.clone();
        cloned.shallow = (Shallow)shallow.clone();
        return cloned;
    }

    public static class Shallow implements Cloneable {
        String shallow = "shallow";

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

    public static void testShallowCopy() {
        ShallowCopy shallowCopy = new ShallowCopy();
        try {
            ShallowCopy shallowCopyClone = (ShallowCopy) shallowCopy.clone();
            LogUtil.info("ShallowCopy", "(shallowCopy == shallowCopyClone) = " + (shallowCopy == shallowCopyClone)); // false
            LogUtil.info("ShallowCopy", "(shallowCopy.equals(shallowCopyClone)) = " + (shallowCopy.equals(shallowCopyClone))); // false
            LogUtil.info("ShallowCopy", "(shallowCopy.shallow == shallowCopyClone.shallow) = " + (shallowCopy.shallow == shallowCopyClone.shallow)); // false
            LogUtil.info("ShallowCopy", "(shallowCopy.shallow.equals(shallowCopyClone.shallow)) = " + (shallowCopy.shallow.equals(shallowCopyClone.shallow))); // false
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

返回结果

2025-03-13 17:14:09.906   (shallowCopy == shallowCopyClone) = false
2025-03-13 17:14:09.906   (shallowCopy.equals(shallowCopyClone)) = false
2025-03-13 17:14:09.906   (shallowCopy.shallow == shallowCopyClone.shallow) = false
2025-03-13 17:14:09.906   (shallowCopy.shallow.equals(shallowCopyClone.shallow)) = false

但这个方法有个弊端,就是如果引用对象的链路比较深,例如 A 引用 B,B 引用 C,C 引用 D,甚至更深,就需要重写很多clone()方法,不易于维护.

值得注意的是Shallow类里面的shallow变量,它是一个String类型的对象引用,但是却可以不用实现clone接口实现深拷贝。原因是String是不可变的,修改String类型都会返回一个新对象,参考文档String 基础知识

  1. 使用Serializable序列化实现深拷贝

需要注意的是引用对象的类型也要实现Serializable接口,不然后报错

public class ShallowCopy implements Cloneable, Serializable {
       public static class Shallow implements Cloneable, Serializable {
           
       }
}

如果Shallow没有实现Serializable接口,则会出现java.io.NotSerializableException。接着使用IO实现序列化和反序列化

    public ShallowCopy deepClone() {
        ShallowCopy deepClone = null;
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bio = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bio);
            deepClone = (ShallowCopy) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        return deepClone;
    }

结果打印

    public static void testDeepCopy() {
        ShallowCopy shallowCopy = new ShallowCopy();
        ShallowCopy shallowCopyDeepClone = shallowCopy.deepClone();
        LogUtil.info("ShallowCopy", "(shallowCopy == shallowCopyDeepClone) = " + (shallowCopy == shallowCopyDeepClone)); // false
        LogUtil.info("ShallowCopy", "(shallowCopy.equals(shallowCopyDeepClone)) = " + (shallowCopy.equals(shallowCopyDeepClone))); // false
        LogUtil.info("ShallowCopy", "(shallowCopy.shallow == shallowCopyDeepClone.shallow) = " + (shallowCopy.shallow == shallowCopyDeepClone.shallow)); // false
        LogUtil.info("ShallowCopy", "(shallowCopy.shallow.equals(shallowCopyDeepClone.shallow)) = " + (shallowCopy.shallow.equals(shallowCopyDeepClone.shallow))); // false
    }