Java 深拷贝和浅拷贝
在浅拷贝中,如果原型对象的成员变量是基本类型时,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。
对应的深拷贝,如果时成员变量为引用对象也复制一份给复制对象。
实现
1、新建一个 Person 和 PersonId 类
public class Person implementsCloneable {privateString name;private intage;privatePersonId personId;public Person(String name, int age, intid) {this.name =name;this.age =age;this.personId = newPersonId(id);
}public void setId(intid) {
personId.setId(id);
}public void setAge(intage) {this.age =age;
}public Person clone() throwsCloneNotSupportedException {
Person cloned= (Person)super.clone();returncloned;
}
@OverridepublicString toString() {return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", personId=" + personId +
'}';
}
}
classPersonId{private intid;public PersonId(intid) {this.id =id;
}public intgetId() {returnid;
}public void setId(intid) {this.id =id;
}
@OverridepublicString toString() {return "PersonId{" +
"id=" + id +
'}';
}
}
2、使用 CloneDemo 类来测试浅拷贝
public classCloneDemo {public static void main(String[] args) throwsCloneNotSupportedException {
Person person= new Person("zhangsan",20,123);
Person cloned=person.clone();
System.out.println("original: "+person);
System.out.println("cloned: "+cloned);
System.out.println("Modify Age and Id: ");
cloned.setAge(55);
cloned.setId(234);
System.out.println("original: "+person);
System.out.println("cloned: "+cloned);
}
}
3、输出
可以看到我们修改了拷贝对象的年龄和ID,原始对象的年龄还是20,原始对象的ID却变成了我们修改后的值,我们并没有对原始对象的ID进行修改,这里说明浅拷贝对于对象仅仅是拷贝了一个对象的引用而已。
4、接下来我们修改 Person 类的 clone() 方法,实现深拷贝。
public Person clone() throwsCloneNotSupportedException {
Person cloned= (Person)super.clone();
cloned.personId=personId.clone();returncloned;
}
5、为了拷贝PersonId 的对象我们需要PersonId 类实现 Cloneable 接口
class PersonId implementsCloneable{private intid;public PersonId(intid) {this.id =id;
}public intgetId() {returnid;
}public void setId(intid) {this.id =id;
}
@OverridepublicString toString() {return "PersonId{" +
"id=" + id +
'}';
}public PersonId clone() throwsCloneNotSupportedException {return (PersonId)super.clone();
}
}
6、再次运行 CloneDemo 输出
可以看到 原始对象的ID 值没有被修改。
7、使用序列化实现深拷贝
import java.io.*;public class Person implementsCloneable, Serializable {privateString name;private intage;privatePersonId personId;public Person(String name, int age, intid) {this.name =name;this.age =age;this.personId = newPersonId(id);
}public void setId(intid) {
personId.setId(id);
}public void setAge(intage) {this.age =age;
}public Person clone() throwsCloneNotSupportedException {try{
ByteArrayOutputStream bout= newByteArrayOutputStream();try(ObjectOutputStream out = newObjectOutputStream(bout)) {
out.writeObject(this);
}try(InputStream bin = newByteArrayInputStream(bout.toByteArray())){
ObjectInputStream in= newObjectInputStream(bin);return(Person)in.readObject();
}
}catch (IOException |ClassNotFoundException e) {
CloneNotSupportedException e2= newCloneNotSupportedException();
e2.initCause(e);throwe2;
}
}
@OverridepublicString toString() {return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", personId=" + personId +
'}';
}
}class PersonId implementsCloneable,Serializable{private intid;public PersonId(intid) {this.id =id;
}public intgetId() {returnid;
}public void setId(intid) {this.id =id;
}
@OverridepublicString toString() {return "PersonId{" +
"id=" + id +
'}';
}public PersonId clone() throwsCloneNotSupportedException {return (PersonId)super.clone();
}
}
所有写入流的对象都要实现 Serializable 接口。将 Person 对象写入流中然后再从流中读取出来实现深拷贝。
输出:
总结
若要实现深拷贝,如果对象中引用了其他对象,必须将引用的对象也克隆。