1.淺複製與深複製概念
(1)淺複製(淺克隆)被複制對象的所有變量都含有與原來對象相同的值,而所有的對其他對象的引用仍然只指向原來的對象,換言之,淺複製僅僅複製鎖考慮的對象,而不復制它所引用的對象。(2)深複製(深克隆)被複制對象的所有變量都含有與原來的對象相同的值,除去那些引用其他對象的變量,那些引用其他對象的變量將指向被複制過的新對象,而不再試原有的那些被引用的對象,換言之,深複製把要複製的對象所引用的對象都複製了一遍。
實現ICloneable接口的方式取決於我們的類型的數據成員。如果類型僅包含值類型(int,byte等類型)和string類型的數據成員,我們只要在Clone方法中初始化一個新的對象,將其的數據成員設置爲當前對象的各個成員的值即可。事實上,object類的MemberwiseClone方法會自動完成該過程。
如果自定義類型包含引用類型的數據成員,必須考慮Clone方法是實現淺拷貝(shallow copy)還是深拷貝(deep copy)。淺拷貝是指副本對象中的引用類型的數據成員與源對象的數據成員指向相同的對象。而如果是深拷貝,則必須創建整個對象的結構,副本對象中的引用類型的數據成員與源對象的數據成員指向不同的對象。
淺拷貝是容易實現的,就是使用前面提到的MemberwiseClone方法。開發人員往往希望使用的類型能夠實現深拷貝,但會發現這樣的類型並不多。這種情況在System.Collections命名空間中尤其常見,這裏面的類在其Clone方法中實現的都是淺拷貝
2. java的clone()方法
(1)clone方法將對象複製了一份並返回給調用之,一般而言,clone()方法滿足:
對任何的對象C=X,都有x.clone()!=x//克隆對象與源對象不是同一個對象
對任何的對象X,都有x.clone().getClass()==x.getClass()//克隆對象與源對象類型一樣
如果對象X的equals()方法定義恰當,那麼x.clone().equals(x)應該成立。
(2)java對象中的克隆
爲了獲取對象的一份拷貝,我們可以利用Object類得clone()方法。
在派生類中覆蓋基類的clone(),並聲明爲public。
在派生類的clone()方法中,調用super.clone().
在派生類中實現Cloneable接口
代碼如下
package clone;
public 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{
o=(Student)super.clone();//Object中的clone()識別出你要複製的哪一個對象
}
catch(CloneNotSupportedException e){
System.out.println(e.toString());
}
return o;
}
public static void main(String[] args){
Student s1 = new Student("zhang",18);
Student s2 = (Student)s1.clone();
s2.name="li";
s2.age=20;
System.out.println("name="+s1.name+","+"age="+s1.age);//修改學生2後不影響學生1的值
}
}
說明:
1問什麼我們在派生類中覆蓋Object的clone方式時,一定要調用super.clone()呢?在運行時刻,Object中的clone()識別出你要複製的是哪一個對象,然後爲此對象分配空間,並進行對象的複製,將原始對象的內容一一複製到新對象的存儲空間中。
繼承自java.lang.Object類得clone()方式是淺複製,以下代碼可以證明
此處淺拷貝的測試代碼如果要運行正確需要把professor類中的o.p=(Professor)p.clone();去掉
package clone;
public class CloneTestPro
{
public static void main(String[] args)
{
Professor p = new Professor("王教授",50);
Student s1 = new Student("張學生",18,p);
System.out.println("s1:" + s1.name + " " + s1.age + " " + s1.p.name+ " " + s1.p.age);
//拷貝一個學生
Student s2 = (Student)s1.clone();
s2.age=21;
s2.name="WUDI";
System.out.println("s2:" + s2.name + " " + s2.age + " "+ s2.p.name+ " " + s2.p.age);
System.out.println("after s2 changing some property:");
//通過學生s2改變了他所在學校的名稱和成立時間
s2.name="Tianjin University";
s2.age=12;
System.out.println("s1:" + s1.name + " " + s1.age + " " + s1.p.name+ " " + s1.p.age);
System.out.println("s2:" + s2.name + " " + s2.age + " " + s2.p.name+ " " + s2.p.age);
//從運行結果可以看出,通過學生s2將其引用school所指向的對象的name和bornYear屬性改變,
//相應的s1的也被改變,這說明學生s1和學生s2裏面的school引用指向的始終是同一個School對象。這就是淺拷貝的含義。
}
}
2.那應該如何實現深層次的克隆,即修改s2的教授不會影響s1的教授?代碼改進如下。
(1)淺拷貝
默認的拷貝都是淺拷貝,你可以通過下面的方法實現:
class Employee implements Cloneable
{
public Employee clone() throws CloneNotSupportedException
{
return super.clone();
}
}
class Employee implements Cloneable
{
public Employee clone() throws CloneNotSupportedException
{
return super.clone();
}
}
淺拷貝需要記住的是原對象的各屬性最好都是基本類型或者不可變的對象。
(2)深拷貝
深拷貝是相對淺拷貝的,解決不能管理子對象的問題的。
實現方法如下:
class Employee implements Cloneable
{
public Object clone() throws CloneNotSupportedException
{
Employee cloned = (Employee)super.clone();
//深拷貝子對象
cloned.dep = (Department)dep.clone();
return cloned.
}
}
值得注意的是 :如果需要使用clone方法,必需實現java.lang.Cloneable接口,否則會拋出java.lang.CloneNotSupportedException。
另外clone方法所做的的操作是直接複製字段的內容,換句話說,這個操作並不管該字段對應的對象實例內容。
像這樣字段對字段的拷貝(field to field copy)就成爲"淺拷貝",clone方法所做的正是"淺拷貝"。