Java 浅拷贝和深拷贝
浅拷贝
浅拷贝会创建一个新的对象,复制该对象的基本类型引用,但不复制该对象内的其他对象引用。一个对象要支持浅拷贝,需要实现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
我们发现shallowCopy
和shallowCopyClone
指向不同的对象,但是里面的shallow
却是同一个。如果要实现两个shallowCopy
和shallowCopyClone
两个完全不同的对象,也即是要实现深拷贝,有两个办法,下文介绍两种方法
深拷贝
- 内部的对象引用所属类也继承
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 基础知识
- 使用
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
}