1.浅拷贝和深拷贝的概念(1)浅拷贝(shallowclone)被拷贝对象的所有变量都包含与原对象相同的值,所有对其他对象的引用仍然指向原对象。换句话说,浅拷贝只复制有问题的对象,而不是它引用的对象。(2)深拷贝(deepclone)复制对象的所有变量都包含与原始对象相同的值,除了那些引用其他对象的变量。引用其他对象的变量将指向被复制的新对象,而不是原始引用的对象。换句话说,深拷贝复制了所有被复制对象引用的对象。2.Java的clone()方法(1)clone方法复制对象并返回给调用者。一般来说,clone()方法满足:①对于任意一个对象x,都有x.clone()!=x//克隆出来的对象和原对象不是同一个对象②对于任意一个对象x,都有x.clone().getClass()==x.getClass()//克隆出来的对象类型与原对象相同③如果正确定义了对象x的equals()方法,则x.clone().equals(x)应该成立。⑵Java中对象的克隆①为了获得一个对象的副本,我们可以使用Object类的clone()方法。②在派生类中重写基类的clone()方法,声明为public。③在派生类的clone()方法中,调用super.clone()。④在派生类中实现Cloneable接口。请看下面的代码:();//Object中的clone()标识了你要复制哪个对象。}catch(CloneNotSupportedExceptione){System.out.println(e.toString());}returno;}publicstaticvoidmain(String[]args){Students1=newStudent("zhangsan",18);Students2=(Student)s1.clone();s2.name="lisi";s2.age=20;//修改学生2后,学生1的值不受影响。System.out.println("name="+s1.name+","+"age="+s1.age");System.out.println("name="+s2.name+","+"age="+s2.age);}}解释:①为什么在派生类中重写Object的clone()方法时一定要调用super.clone()?在运行时,Object中的clone()识别出你要复制的是哪个对象,然后为这个对象分配空间,并复制对象,将原对象的内容一个一个复制到新对象的存储空间中。②继承自java.lang.Object类的clone()方法是浅拷贝。下面的代码可以证明这一点。classProfessor{Stringname;intage;Professor(Stringname,intage){this.name=name;this.age=age;}}publicclassStudentimplementsCloneable{Stringname;//常量对象。intage;Professorp;//学生1和学生2的参考值相同。学生(Stringname、intage、Professorp){this.name=name;this.age=age;this.p=p;}publicObjectclone(){Studentto=null;try{o=(Student)super.clone();}catch(CloneNotSupportedExceptione){System.out.println(e.toString());}o.p=(Professor)p.clone();returno;}publicstaticvoidmain(String[]args){Professorp=newProfessor("wangwu",50);Students1=newStudent("zhangsan",18,p);Students2=(Student)s1.clone();s2.p.name="lisi";s2.p.age=30;System.out.println("name="+s1.p.name+","+"age="+s1.p.age);System.out.println("name="+s2.p.name+","+"age="+s2.p.age);//输出结果学生1和2的教授变成了lisi,年龄为30。}}那么如何实现深度克隆,即修改s2的教授,不会影响s2的教授s1?代码改进如下。改进,使学生1的教授不改变(深度克隆){o=super.clone();}catch(CloneNotSupportedExceptione){System.out.println(e.toString());}returno;}}publicclassStudentimplementsCloneable{Stringname;intage;Professorp;Student(Stringname,intage,Professorp){这个.name=name;this.age=age;this.p=p;}publicObjectclone(){Studento=null;try{o=(Student)super.clone();}catch(CloneNotSupportedExceptione){System.out.println(e.toString());}//复制引用对象o.p=(Professor)p.clone();returno;}publicstaticvoidmain(String[]args){Professorp=newProfessor("wangwu",50);Students1=newStudent("zhangsan",18,p);Students2=(Student)s1.clone();s2.p.name="lisi";s2.p.age=30;//学生1的教授是不变。System.out.println("name="+s1.p.name+","+"age="+s1.p.age);System.out.println("name="+s2.p.name+","+"age="+s2.p.age);}}3.使用序列化做深拷贝(主要是为了避免重写更复杂对象的深拷贝的clone()方法,也可以实现断点续传等功能)。将对象写入流的过程是串行的序列化(Serilization)过程,但在Java程序员圈子里被非常形象地称为“冻结”或“挑选”过程;而从流中读取对象的并行化(Deserialization)过程称为“Thawing”或“depicking”过程。需要指出的是,stream中写入的是对象的副本,原来的对象在JVM中还存在,所以“pickled”的只是对象的副本,Javapickles还是可以保鲜的.Java语言中要深拷贝一个对象,往往可以先让对象实现Serializable接口,然后将对象(实际上只是对象的一个??拷贝)写入流(pickleitintoapickle),然后读取从流中(将pickle放回Fresh),可以重建对象。以下是深拷贝源码。publicObjectdeepClone(){//将对象写入流中ByteArrayOutoutStreambo=newByteArrayOutputStream();ObjectOutputStreamoo=newObjectOutputStream(bo);oo.writeObject(this);//从流中读取ObjectInputStreamoi=newObjectInputStream(bi);return(oi.readObject());}这样做的前提是对象和对象内部所有引用的对象都是可序列化的,否则,需要仔细检查那些不可序列化的是否是对象或对象的属性可以设置为瞬态,从而将其排除在复制过程之外。上述示例代码改进如下。classTeacherimplementsSerializable{Stringname;intage;publicvoidTeacher(Stringname,intage){this.name=name;this.age=age;}}publicclassStudentimplementsSerializable{Stringname;//常量对象intage;Teachert;//学生1的参考值和学生2都是一样的。publicvoidStudent(Stringname,intage,Teachert){this.name=name;this.age=age;this.p=p;}publicObjectdeepClone()throwsIOException,OptionalDataException,ClassNotFoundException{//将对象写入流ByteArrayOutoutStreambo=newByteArrayOutputStream();ObjectOutputStreamoo=newObjectOutputStream(bo);oo.writeObject(this);//读取ByteArrayInputStreambi=newByteArrayInputStream(bo.toByteArray());ObjectInputStreamoi=newObjectInputStream(bi);return(oi.readObject());}publicstaticvoidmain(String[]args){Teachert=newTeacher("tangliang",30);Students1=newStudent("zhangsan",18,t);Students2=(Student)s1.deepClone();s2.t.name="tony";s2.t.age=40;//学生1的老师不变System.out.println("name="+s1.t.name+","+"age="+s1.t.age);}}
