1. clone方法簡介
clone方法返回與當前對象的一個副本對象。可以通過操作副本對象而不影響當前對象。
使用clone方法需要實現Cloneable接口。並重寫Object方法中的clone方法。
需要注意的是在clone在Object中是project修飾符。因為所有類都是Object的子類,所以如果不實現clone方法,在類中可以直接使用父類的clone方法,但是其對象在別的類中不能調用clone方法。所以必須重寫clone方法。
如果不實現Cloneable接口,只重寫clone方法,調用則會拋出異常。
Stu類未實現Cloneable接口所以拋出異常
2. 淺克隆與深克隆
2.1 淺克隆
看下面一個例子
1 import java.util.Arrays; 2 import java.util.HashSet; 3 import java.util.Scanner; 4 import java.util.Set; 5 6 public class Test { 7 8 public static void main(String[] args) throws CloneNotSupportedException { 9 Stu stu = new Stu(); 10 stu.name = "Tom"; 11 Stu stu1; 12 stu1 = stu; 13 System.out.println(stu==stu1); 14 stu1.name = "LiMing"; 15 System.out.println("stu:"+stu.name+" stu1:"+stu1.name); 16 stu1 = (Stu) stu.clone(); 17 System.out.println(stu==stu1); 18 stu1.name = "Tom"; 19 System.out.println("stu:"+stu.name+" stu1:"+stu1.name); 20 } 21 22 } 23 class Stu implements Cloneable{ 24 String name; 25 @Override 26 protected Object clone() throws CloneNotSupportedException { 27 // TODO Auto-generated method stub 28 return super.clone(); 29 } 30 31 }
Stu類實現了Cloneable接口,並且重寫了clone方法。
首先設stu1變量值等於stu,我們都知道stu1直接指向stu的地址。所以測試stu1==stu時,結果為true,說明兩個地址相同。同時改變stu1的name值為“LiMing”,stu的name值也改為“LiMimg”。
設stu1變量值等於stu.clone(),這時stu1的指向stu對象副本的地址,測試stu1==stu,結果為false,說明兩個地址不同。改變stu1的name值為"Tom",stu因為指向的對象與stu1的指向的對象不同,因此stu的name值並未改變。
測試結果
2.2 深克隆
考慮下面這種情況
學生類包含筆類,這時在使用clone方法。
1 public class Test { 2 3 public static void main(String[] args) throws CloneNotSupportedException { 4 Stu stu = new Stu(); 5 stu.name = "Tom"; 6 stu.pen.name = "A"; 7 Stu stu1; 8 stu1 = (Stu) stu.clone(); 9 System.out.println(stu1.pen==stu.pen); 10 stu1.pen.name = "B"; 11 System.out.println("stu1:"+stu1.pen.name+"|stu:"+stu.pen.name); 12 } 13 14 } 15 class Stu implements Cloneable{ 16 String name; 17 Pen pen = new Pen(); 18 @Override 19 protected Object clone() throws CloneNotSupportedException { 20 // TODO Auto-generated method stub 21 return super.clone(); 22 } 23 24 } 25 class Pen{ 26 String name; 27 }
發現此時stu1.pen的地址與stu.pen的地址相同。所以改變stu1.pen.name的值,stu.pen.name的值也發生了相應的改變。這是因為對於引用類型,clone方法只克隆引用類型的地址。這時就用到了深克隆。
測試結果
深克隆:
1 public class Test { 2 3 public static void main(String[] args) throws CloneNotSupportedException { 4 Stu stu = new Stu(); 5 stu.name = "Tom"; 6 stu.pen.name = "A"; 7 Stu stu1; 8 stu1 = (Stu) stu.clone(); 9 System.out.println(stu1.pen==stu.pen); 10 stu1.pen.name = "B"; 11 System.out.println("stu1:"+stu1.pen.name+"|stu:"+stu.pen.name); 12 } 13 14 } 15 class Stu implements Cloneable{ 16 String name; 17 Pen pen = new Pen(); 18 @Override 19 protected Object clone() throws CloneNotSupportedException { 20 // TODO Auto-generated method stub 21 Stu temp = (Stu) super.clone(); 22 temp.pen = (Pen) pen.clone(); 23 return temp; 24 } 25 26 } 27 class Pen implements Cloneable{ 28 String name; 29 @Override 30 protected Object clone() throws CloneNotSupportedException { 31 // TODO Auto-generated method stub 32 return super.clone(); 33 } 34 }
此時Pen類也重寫了Cloneable方法。同時在重寫Stu類的clone方法時先克隆Pen,在返回temp,這樣temp中的pen指向克隆后的地址。
運行結果stu1.pen的地址與stu.pen的地址不在相同,改變stu1.pen.name的值而stu.pen.name的值不在改變。stu1.pen對象是一個全新的副本。
測試結果: