Java深克隆与浅克隆

来源:互联网 发布:淘宝网店保证金怎么交 编辑:程序博客网 时间:2024/05/16 09:04

概念理解:

浅复制(浅克隆shallow clone:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换言之,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象

深复制(深克隆Deep clone:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换言之,深复制把要复制的对象所引用的对象都复制了一遍

Java的clone方法是定义在JDK的Object类中


clone方法将对象复制了一份并返回给调用者。一般而言,clone()方法满足:

①对任何的对象x,都有x.clone() !=x

•克隆对象与原对象不是同一个对象


②对任何的对象x,都有x.clone().getClass()= =x.getClass()

•克隆对象与原对象的类型一样


③如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。


一些说明:
–①为什么我们在派生类中覆盖Object的clone()方法时,一定要调用super.clone()呢?
•在运行时刻,Object中的clone()识别出你要复制的是哪一个对象,然后为此对象分配空间,并进行对象的复制,将原始对象的内容一一复制到新对象的存储空间中。


–②继承自java.lang.Object类的clone()方法是浅复制

2.实验代码:(浅克隆)

1.测试clone对象引用与原对象引用不是指向同一对象
public class CloneTest {public static void main(String[] args) throws CloneNotSupportedException {//创建一个对象s1Student s1 = new Student(20,"villa");//Clone s1 给s2Student s2 = (Student)s1.clone();//检验s1与s2是不是指向同一个对象System.out.println("s2.name= "+s2.getSname());System.out.println("s2.age= "+s2.getSage());System.out.println("---------------------");//将s1的name变为"tony"s1.setSname("tony");System.out.println("s1.name= "+s1.getSname());System.out.println("s2.name= "+s2.getSname());System.out.println("---------------------");System.out.println(s1==s2);}}class Student implements Cloneable{private int sage;private String sname;public Student() {}public Student(int age,String name) {this.sage = age;this.sname = name;}public int getSage() {return sage;}public void setSage(int sage) {this.sage = sage;}public String getSname() {return sname;}public void setSname(String sname) {this.sname = sname;}@Overrideprotected Object clone() throws CloneNotSupportedException {// TODO Auto-generated method stubreturn super.clone();}}
结果输出:
s2.name= villas2.age= 20---------------------s1.name= tonys2.name= villa---------------------false

2.测试含有引用对象的克隆
①浅克隆不能复制引用对象,复制后的对象与原对象使用同一个引用对象
说明:加入一个Teacher类,并在student类中加入teacher的引用
public class DeepCloneTest {public static void main(String[] args) throws CloneNotSupportedException {//创建一个Teacher对象Teacher teacher = new Teacher(50,"david");//创建一个Student2对象,并将teacher赋给它Student2 ss1 = new Student2(20,"rooney",teacher);//克隆ss1Student2 ss2 = (Student2)ss1.clone();//把ss1的name改为"linda",其teacher的name改为"robert"ss1.setSname("linda");ss1.getTeacher().setTname("robert");//测试System.out.println("ss2.name= "+ss2.getSname());System.out.println(ss1==ss2);System.out.println("-------------------------");System.out.println("ss1.teacher.name= "+ss1.getTeacher().getTname());System.out.println("ss2.teacher.name= "+ss2.getTeacher().getTname());System.out.println(ss1.getTeacher()==ss2.getTeacher());}}class Student2 implements Cloneable{private int sage;private String sname;private Teacher teacher;public Student2() {}public Student2(int age,String name,Teacher teacher) {this.sage = age;this.sname = name;this.teacher = teacher;}public int getSage() {return sage;}public void setSage(int sage) {this.sage = sage;}public String getSname() {return sname;}public void setSname(String sname) {this.sname = sname;}public Teacher getTeacher() {return teacher;}public void setTeacher(Teacher teacher) {this.teacher = teacher;}@Overrideprotected Object clone() throws CloneNotSupportedException {// TODO Auto-generated method stubreturn super.clone();}}class Teacher{private int tage;private String tname;public Teacher() {}public Teacher(int age,String name) {this.tage = age;this.tname = name;}public int getTage() {return tage;}public void setTage(int tage) {this.tage = tage;}public String getTname() {return tname;}public void setTname(String tname) {this.tname = tname;}}
结果输出:
ss2.name= rooneyfalse-------------------------ss1.teacher.name= robertss2.teacher.name= roberttrue
结果说明:ss1和ss2不是同一个对象,但ss1的teacher对象和ss2的teacher对象是同一个对象,clone的时候没有复制teacher对象,以上就是所谓的浅克隆
那么假如想复制的时候也同时复制引用对象teacher,可以使用一下的方法:

②深克隆

代码说明:①让Teacher类实现Cloneable接口,重写clone方法②在student的clone()方法中,也同时复制teacher对象
public class DeepCloneTest {public static void main(String[] args) throws CloneNotSupportedException {//创建一个Teacher对象Teacher teacher = new Teacher(50,"david");//创建一个Student2对象,并将teacher赋给它Student2 ss1 = new Student2(20,"rooney",teacher);//克隆ss1Student2 ss2 = (Student2)ss1.clone();//把ss1的name改为"linda",其teacher的name改为"robert"ss1.setSname("linda");ss1.getTeacher().setTname("robert");//测试System.out.println("ss2.name= "+ss2.getSname());System.out.println(ss1==ss2);System.out.println("-------------------------");System.out.println("ss1.teacher.name= "+ss1.getTeacher().getTname());System.out.println("ss2.teacher.name= "+ss2.getTeacher().getTname());System.out.println(ss1.getTeacher()==ss2.getTeacher());}}class Student2 implements Cloneable{private int sage;private String sname;private Teacher teacher;public Student2() {}public Student2(int age,String name,Teacher teacher) {this.sage = age;this.sname = name;this.teacher = teacher;}public int getSage() {return sage;}public void setSage(int sage) {this.sage = sage;}public String getSname() {return sname;}public void setSname(String sname) {this.sname = sname;}public Teacher getTeacher() {return teacher;}public void setTeacher(Teacher teacher) {this.teacher = teacher;}@Overrideprotected Object clone() throws CloneNotSupportedException {Student2 ss = (Student2)super.clone();ss.setTeacher((Teacher)ss.getTeacher().clone());return ss;}}class Teacher implements Cloneable{private int tage;private String tname;public Teacher() {}public Teacher(int age,String name) {this.tage = age;this.tname = name;}public int getTage() {return tage;}public void setTage(int tage) {this.tage = tage;}public String getTname() {return tname;}public void setTname(String tname) {this.tname = tname;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}}
结果输出:
ss2.name= rooneyfalse-------------------------ss1.teacher.name= robertss2.teacher.name= davidfalse


上述就可以实现对象的深克隆,但是这样做有什么缺点或者不方便的地方呢?我们使用JDK提供给我的clone方法,当一个对象含有多个对象的引用的时候,重写clone方法就显得非常的麻烦。

3.利用序列化来做深克隆

问题的关键出在对象的引用上,很自然就会想到java的序列化机制跟这个很相似,事实上我们可以利用序列化来做深克隆。
概念说明:
①把对象写到流里的过程是序列化(Serilization)过程,而把对象从流中读出来的过程则叫做反序列化(Deserialization)过程。应当指出的是,写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。
②在Java语言里深复制一个对象,常常可以先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象。
③这样做的前提是对象以及对象内部所有引用到的对象都是可串行化的,否则,就需要仔细考察那些不可串行化的对象可否设成transient,从而将之排除在复制过程之外。
注意:Cloneable与Serializable都是marker Interface,也就是说他们只是一个标识接口,没有定义任何方法。

实验代码: 

1.代码说明:不再实现cloneable接口,不再重写clone方法,现在我们实现Serializable接口,利用序列化机制,先将对象写入文件,然后再反序列化读出,由于序列化时会一并序列化里面的引用所以利用这一点进行深克隆。
package com.dr.test.clone;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;public class SerializableClone {public static void main(String[] args) throws Exception {//创建一个Teacher对象Teacher3 teacher = new Teacher3(50,"david");//创建一个Student2对象,并将teacher赋给它Student3 ss = new Student3(20,"rooney",teacher);//克隆ss1Student3 ss2 = (Student3)ss.deepClone();//把ss1的name改为"linda",其teacher的name改为"robert"ss.setSname("linda");ss.getTeacher().setTname("robert");//测试System.out.println("ss2.name= "+ss2.getSname());System.out.println(ss==ss2);System.out.println("-------------------------");System.out.println("ss.teacher.name= "+ss.getTeacher().getTname());System.out.println("ss2.teacher.name= "+ss2.getTeacher().getTname());System.out.println(ss.getTeacher()==ss2.getTeacher());}}class Student3 implements Serializable{private static final long serialVersionUID = 1L;private int sage;private String sname;private Teacher3 teacher;public Student3() {}public Student3(int age,String name,Teacher3 teacher) {this.sage = age;this.sname = name;this.teacher = teacher;}public int getSage() {return sage;}public void setSage(int sage) {this.sage = sage;}public String getSname() {return sname;}public void setSname(String sname) {this.sname = sname;}public Teacher3 getTeacher() {return teacher;}public void setTeacher(Teacher3 teacher) {this.teacher = teacher;}public Object deepClone() throws Exception {ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(this);ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);return ois.readObject();}}class Teacher3 implements Serializable{private int tage;private String tname;public Teacher3() {}public Teacher3(int age,String name) {this.tage = age;this.tname = name;}public int getTage() {return tage;}public void setTage(int tage) {this.tage = tage;}public String getTname() {return tname;}public void setTname(String tname) {this.tname = tname;}}
结果输出:
ss2.name= rooneyfalse-------------------------ss.teacher.name= robertss2.teacher.name= davidfalse


还可以去进一步验证,当Student3含有多个对象引用时,也能完成深克隆。
原创粉丝点击