Java 深拷贝和浅拷贝 在浅拷贝中,如果原型对象的成员变量是基本类型时,将复制一份给克隆对象;如果原型对象的成员变量是引用类型,则将引用对象的地址复制一份给克隆对象,也就是说原型对象和克隆对象的成员变量指向相同的内存地址。
对应的深拷贝,如果时成员变量为引用对象也复制一份给复制对象。
实现
1、新建一个 Person 和 PersonId 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 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 类来测试浅拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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() 方法,实现深拷贝。
1 2 3 4 5 public Person clone () throws CloneNotSupportedException { Person cloned = (Person)super .clone(); cloned.personId = personId.clone(); return cloned; }
5、为了拷贝PersonId 的对象我们需要PersonId 类实现 Cloneable 接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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、使用序列化实现深拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 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 对象写入流中然后再从流中读取出来实现深拷贝。
输出:
总结
若要实现深拷贝,如果对象中引用了其他对象,必须将引用的对象也克隆。