標籤:
1.淺拷貝與深拷貝概念
(1)淺拷貝(淺複製)
淺拷貝又叫淺複製,將對象中的所有欄位複製到新的對象(副本)中。其中,實值型別欄位(java中8中原始類型)的值被複製到副本中後,在副本中的修改不會影響到來源物件對應的值。而參考型別的欄位被複製到副本中的還是參考型別的引用,而不是引用的對象,在副本中對參考型別的欄位值做修改會影響到來源物件本身。
淺拷貝簡單歸納就是只複製一個對象,對象內部存在指向其他對象,數組或引用則不複製。
(2)深拷貝(深複製)
將對象中的所有欄位複製到新的對象中。不過,無論是對象的實值型別欄位,還是參考型別欄位,都會被重新建立並賦值,對於副本的修改,不會影響到來源物件本身。
深拷貝簡單歸納就是對象內部引用的對象均複製。
2.Java中的clone()方法
(1)clone()方法將對象複製了一份並返回給調用者。一般而言,clone()方法滿足下面規範:
①對任何的對象x,都有x.clone() != x;//複製對象與原對象不是同一個對象
②對任何的對象x,都有x.clone().getClass()== x.getClass();//複製對象與原對象的類型一樣
③如果對象x的equals()方法定義恰當,那麼x.clone().equals(x);應該成立。
(2)Java中對象的複製
clone()方法是在Object中定義的,而且是protected的,只有實現了Cloneable介面的類才可以在其執行個體上調用clone()方法,否則會拋出CloneNotSupportException。
為了擷取對象的一份拷貝,我們可以利用Object類的clone()方法,也可以實現Cloneable介面,覆蓋基類的clone()方法,在clone()方法中,調用super.clone()。
Cloneable介面是一個標記介面,也就是沒有任何內容,定義如下:
package java.lang;
pubilc interface Cloneable{
}
例子代碼如下:
class Student implements Cloneable { String name; int age; Student(String name,int age){ this.name=name; this.age=age; } public Object clone(){ Object o=null; try{ //Object中的clone()識別出你要複製的是哪一個對象 o=(Student)super.clone(); }catch(CloneNotSupportedException e){ System.out.println(e.toString()); } return o; } } public static void main(String[] args){ Student s1=new Student("zhangsan",18); Student s2=(Student)s1.clone(); s2.name="lisi"; s2.age=20; System.out.println("name="+s1.name+","+"age="+s1.age);//修改學生2後,不影響學生1的值。 }
說明:
①為什麼我們在衍生類別中覆蓋Object的clone()方法時,一定要調用super.clone()呢?
在運行時刻,Object中的clone()識別出你要複製的是哪一個對象,然後為此對象分配空間,並進行對象的複製,將原始對象的內容一一複製到新對象的儲存空間中。
②繼承自java.lang.Object類的clone()方法是淺複製。以下代碼可以證明:
//Professor沒有實現Cloneable介面,預設使用java.lang.Object類的clone()方法 class Professor{ String name; int age; Professor(String name,int age){ this.name=name; this.age=age; } } //Student實現了Cloneable介面 class Student implements Cloneable{ String name;//常量對象。 int age; Professor p; Student(String name,int age,Professor p){ this.name=name; this.age=age; this.p=p; } public Object clone(){ Student o=null; try{ o=(Student)super.clone(); }catch(CloneNotSupportedException e){ System.out.println(e.toString()); } //使用Object類的clone()方法 o.p=(Professor)this.p.clone(); return o; } } public static void main(String[] args){ Professor p=new Professor("wangwu",50); Student s1=new Student("zhangsan",18,p); Student s2=(Student)s1.clone(); s2.p.name="lisi"; s2.p.age=30; //學生1的教授也變成了lisi,age為30 System.out.println("name="+s1.p.name+","+"age="+s1.p.age); }
那應該如何?深層次的複製,即修改s2的教授不會影響s1的教授?代碼改進如下:
//Professor類實現了Cloneable介面,不再使用Object預設的clone()方法 class Professor implements Cloneable{ String name; int age; Professor(String name,int age){ this.name=name; this.age=age; } public Object clone(){ Object o=null; try{ o=super.clone(); }catch(CloneNotSupportedException e){ System.out.println(e.toString()); } return o; } } class Student implements Cloneable{ String name; int age; Professor p; Student(String name,int age,Professor p){ this.name=name; this.age=age; this.p=p; } public Object clone(){ Student o=null; try{ o=(Student)super.clone(); }catch(CloneNotSupportedException e){ System.out.println(e.toString()); } //調用Professor類的clone()方法實現深拷貝 o.p=(Professor)this.p.clone(); return o; } } public static void main(String[] args){ Professor p=new Professor("wangwu",50); Student s1=new Student("zhangsan",18,p); Student s2=(Student)s1.clone(); s2.p.name="lisi"; s2.p.age=30; //學生1的教授不改變 System.out.println("name="+s1.p.name+","+"age="+s1.p.age); }
3.利用序列化來做深複製
把對象寫到流裡的過程是序列化(Serilization)過程,又叫對象序列化,而把對象從流中讀出來的(Deserialization)過程叫還原序列化。應當指出的是,寫在流裡的是對象的一個拷貝,而原對象仍然存在於JVM裡面,因此在Java語言裡深複製一個對象,常常可以先使對象實現Serializable介面,然後把對象(實際上只是對象的一個拷貝)寫到一個流裡,再從流裡讀出來便可以重建對象。
如下為深複製原始碼:
public Object deepClone(){ //將對象寫到流裡 ByteArrayOutoutStream bo=new ByteArrayOutputStream(); ObjectOutputStream oo=new ObjectOutputStream(bo); oo.writeObject(this); //從流裡讀出來 ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi=new ObjectInputStream(bi); return(oi.readObject()); } 這樣做的前提是對象以及對象內部所有引用到的對象都是可序列化的,否則,就需要仔細考察那些不可序列化的對象可否設成transient,從而將之排除在複製過程之外。上例代碼改進如下。 class Professor implements Serializable{ String name; int age; Professor(String name,int age){ this.name=name; this.age=age; } } class Student implements Serializable{ String name; int age; Professor p; Student(String name,int age,Professor p){ this.name=name; this.age=age; this.p=p; } public Object deepClone() throws IOException,OptionalDataException,ClassNotFoundException{ //將對象寫到流裡 ByteArrayOutputStream bo=new ByteArrayOutputStream(); ObjectOutputStream oo=new ObjectOutputStream(bo); oo.writeObject(this);// object of studnet / /從流裡讀出來 ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi=new ObjectInputStream(bi); return(oi.readObject()); } public static void main(String[] args){ Professor p=new Professor("wangwu",50); Student s1=new Student("zhangsan",18,p); Student s2=(Student)s1.deepClone(); s2.p.name="lisi"; s2.p.age=30; //學生1的教授不改變 System.out.println("name="+s1.p.name+","+"age="+s1.p.age); } }
Java中的深拷貝和淺拷貝