Java 深拷贝和浅拷贝
在浅拷贝中,如果原型对象的成员变量是基本类型时,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。
对应的深拷贝,如果时成员变量为引用对象也复制一份给复制对象。
实现
1、新建一个 Person 和 PersonId 类
public class Person implements Cloneable { private String name; private int age; private PersonId personId; public Person(String name, int age, int id) { this.name = name; this.age = age; this.personId = new PersonId(id); } public void setId(int id) { personId.setId(id); } public void setAge(int age) { this.age = age; } public Person clone() throws CloneNotSupportedException { Person cloned = (Person)super.clone(); return cloned; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", personId=" + personId + '}'; } }
class PersonId{ private int id; public PersonId(int id) { this.id = id; } public int getId() { return id; } public void setId(int id) { this.id = id; } @Override public String toString() { return "PersonId{" + "id=" + id + '}'; } }
2、使用 CloneDemo 类来测试浅拷贝
public class CloneDemo { public static void main(String[] args) throws CloneNotSupportedException { 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() throws CloneNotSupportedException { Person cloned = (Person)super.clone(); cloned.personId = personId.clone(); return cloned; }
5、为了拷贝PersonId 的对象我们需要PersonId 类实现 Cloneable 接口
class PersonId implements Cloneable{ private int id; public PersonId(int id) { this.id = id; } public int getId() { return id; } public void setId(int id) { this.id = id; } @Override public String toString() { return "PersonId{" + "id=" + id + '}'; } public PersonId clone() throws CloneNotSupportedException { return (PersonId)super.clone(); } }
6、再次运行 CloneDemo 输出
可以看到 原始对象的ID 值没有被修改。
7、使用序列化实现深拷贝
import java.io.*; public class Person implements Cloneable, Serializable { private String name; private int age; private PersonId personId; public Person(String name, int age, int id) { this.name = name; this.age = age; this.personId = new PersonId(id); } public void setId(int id) { personId.setId(id); } public void setAge(int age) { this.age = age; } public Person clone() throws CloneNotSupportedException { try { ByteArrayOutputStream bout = new ByteArrayOutputStream(); try(ObjectOutputStream out = new ObjectOutputStream(bout)) { out.writeObject(this); } try(InputStream bin = new ByteArrayInputStream(bout.toByteArray())){ ObjectInputStream in = new ObjectInputStream(bin); return (Person)in.readObject(); } } catch (IOException | ClassNotFoundException e) { CloneNotSupportedException e2 = new CloneNotSupportedException(); e2.initCause(e); throw e2; } } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", personId=" + personId + '}'; } } class PersonId implements Cloneable,Serializable{ private int id; public PersonId(int id) { this.id = id; } public int getId() { return id; } public void setId(int id) { this.id = id; } @Override public String toString() { return "PersonId{" + "id=" + id + '}'; } public PersonId clone() throws CloneNotSupportedException { return (PersonId)super.clone(); } }
所有写入流的对象都要实现 Serializable 接口。将 Person 对象写入流中然后再从流中读取出来实现深拷贝。
输出:
总结
若要实现深拷贝,如果对象中引用了其他对象,必须将引用的对象也克隆。