- 實現 Cloneable 接口,重寫 clone() 方法。
- 不實現 Cloneable 接口,會報 CloneNotSupportedException 異常。
package constxiong.interview; /** * 測試克隆 * @author ConstXiong * @date 2019-06-18 11:21:21 */ public class TestClone { public static void main(String[] args) throws CloneNotSupportedException { Person p1 = new Person(1, "ConstXiong");//創建對象 Person p1 Person p2 = (Person)p1.clone();//克隆對象 p1 p2.setName("其不答");//修改 p2的name屬性,p1的name未變 System.out.println(p1); System.out.println(p2); } } /** * 人 * @author ConstXiong * @date 2019-06-18 11:54:35 */ class Person implements Cloneable { private int pid; private String name; public Person(int pid, String name) { this.pid = pid; this.name = name; System.out.println("Person constructor call"); } public int getPid() { return pid; } public void setPid(int pid) { this.pid = pid; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } @Override public String toString() { return "Person [pid:"+pid+", name:"+name+"]"; } }
打印結果
Person constructor call
Person [pid:1, name:ConstXiong]
Person [pid:1, name:其不答]
- Object 的 clone() 方法是淺拷貝,即如果類中屬性有自定義引用類型,只拷貝引用,不拷貝引用指向的對象。
可以使用下面的兩種方法,完成 Person 對象的深拷貝。
方法1、對象的屬性的Class 也實現 Cloneable 接口,在克隆對象時也手動克隆屬性。
@Override public Object clone() throws CloneNotSupportedException { DPerson p = (DPerson)super.clone(); p.setFood((DFood)p.getFood().clone()); return p; }
完整代碼
package constxiong.interview; /** * 測試克隆 * @author ConstXiong * @date 2019-06-18 11:21:21 */ public class TestManalDeepClone { public static void main(String[] args) throws Exception { DPerson p1 = new DPerson(1, "ConstXiong", new DFood("米飯"));//創建Person 對象 p1 DPerson p2 = (DPerson)p1.clone();//克隆p1 p2.setName("其不答");//修改p2的name屬性 p2.getFood().setName("面條");//修改p2的自定義引用類型 food 屬性 System.out.println(p1);//修改p2的自定義引用類型 food 屬性被改變,p1的自定義引用類型 food 屬性也隨之改變,說明p2的food屬性,只拷貝了引用,沒有拷貝food對象 System.out.println(p2); } } class DPerson implements Cloneable { private int pid; private String name; private DFood food; public DPerson(int pid, String name, DFood food) { this.pid = pid; this.name = name; this.food = food; System.out.println("Person constructor call"); } public int getPid() { return pid; } public void setPid(int pid) { this.pid = pid; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public Object clone() throws CloneNotSupportedException { DPerson p = (DPerson)super.clone(); p.setFood((DFood)p.getFood().clone()); return p; } @Override public String toString() { return "Person [pid:"+pid+", name:"+name+", food:"+food.getName()+"]"; } public DFood getFood() { return food; } public void setFood(DFood food) { this.food = food; } } class DFood implements Cloneable{ private String name; public DFood(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }
打印結果
Person constructor call Person [pid:1, name:ConstXiong, food:米飯] Person [pid:1, name:其不答, food:面條]
方法2、結合序列化(JDK java.io.Serializable 接口、JSON格式、XML格式等),完成深拷貝
結合 java.io.Serializable 接口,完成深拷貝
package constxiong.interview; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class TestSeriazableClone { public static void main(String[] args) { SPerson p1 = new SPerson(1, "ConstXiong", new SFood("米飯"));//創建 SPerson 對象 p1 SPerson p2 = (SPerson)p1.cloneBySerializable();//克隆 p1 p2.setName("其不答");//修改 p2 的 name 屬性 p2.getFood().setName("面條");//修改 p2 的自定義引用類型 food 屬性 System.out.println(p1);//修改 p2 的自定義引用類型 food 屬性被改變,p1的自定義引用類型 food 屬性未隨之改變,說明p2的food屬性,只拷貝了引用和 food 對象 System.out.println(p2); } } class SPerson implements Cloneable, Serializable { private static final long serialVersionUID = -7710144514831611031L; private int pid; private String name; private SFood food; public SPerson(int pid, String name, SFood food) { this.pid = pid; this.name = name; this.food = food; System.out.println("Person constructor call"); } public int getPid() { return pid; } public void setPid(int pid) { this.pid = pid; } public String getName() { return name; } public void setName(String name) { this.name = name; } /** * 通過序列化完成克隆 * @return */ public Object cloneBySerializable() { Object obj = null; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(this); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); obj = ois.readObject(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return obj; } @Override public String toString() { return "Person [pid:"+pid+", name:"+name+", food:"+food.getName()+"]"; } public SFood getFood() { return food; } public void setFood(SFood food) { this.food = food; } } class SFood implements Serializable { private static final long serialVersionUID = -3443815804346831432L; private String name; public SFood(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
打印結果
Person constructor call Person [pid:1, name:ConstXiong, food:米飯] Person [pid:1, name:其不答, food:面條]