做開發很少用到克隆的。我能想得到的是用於調用方法時作為參數傳遞,為了保證方法調用前後對象的內部結構不被破壞,可以克隆一個對象作為參數傳遞。
有人可能注意到 Object 類中有一個 native 方法clone
protected native Object clone() throws CloneNotSupportedException;
訪問修飾符是 protected,缺省的情況下Object 及其子類對象無法在別的類中訪問 clone(),等同於所有類缺省沒有克隆能力。
要具備克隆能力,必須實現 Cloneable 接口:
public interface Cloneable {}
奇怪的是,這個接口是空的。然而不用想那麼多,這只是個標記而已,同為標記接口的還有 java.io.Serializable 等。
Cloneable 存在有兩個理由:
要具備克隆能力,必須重寫父類的 clone() 方法,同時將訪問修飾符改為 public,必須使用 super.clone() 進行(淺)克隆。
Object 中的 clone() 識別你要復制的是哪一個對象,然後為此對象分配空間,並進行對象的復制,將原始對象的內容一一復制到新對象的存儲空間中。
需要注意的是這裡的復制是淺層復制(淺層克隆 shadow clone),下面舉一個淺層復制的例子:
public class Student implements Cloneable{ private String name; private int age; private Teacher teacher; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Teacher getTeacher() { return teacher; } public void setTeacher(Teacher teacher) { this.teacher = teacher; } @Override public String toString() { String str = "姓名:" + getName() + ",年齡:" + getAge() + ",老師:" + ((getTeacher()==null)?"未知":getTeacher().getName()); return str; } public Object clone(){ try { return super.clone(); } catch (Exception e) { return null; } } }
public class Teacher { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
public class CloneTest1 { public static void main(String[] args) { Student s1 = new Student(); s1.setAge(10); s1.setName("Li"); Teacher teacher = new Teacher(); teacher.setName("Wu"); s1.setTeacher(teacher); System.out.println(s1.toString()); Student s2 = (Student) s1.clone(); System.out.println(s2.toString()); s1.setAge(20); s1.setName("Hu"); teacher.setName("Yang"); System.out.println(s2.toString()); } }
輸出為:
姓名:Li,年齡:10,老師:Wu 姓名:Li,年齡:10,老師:Wu 姓名:Li,年齡:10,老師:Yang
s1.setAge(20) 和 s1.setName("Hu") 都沒有影響到克隆對象 s2。為什麼? 這裡說說我的理解
基本數據類型或裝箱基本數據類型在方法中作為參數傳遞的時候,都是傳遞的值得拷貝,所以單從它來講已經做到了深層克隆。
String 類型你可以理解為是不可變的,一旦你做了改變(比如使用連接符做拼接),它也就變成另外一個對象了,不會影響到原對象,所以單從它來講也做到了深層克隆。
teacher.setName("Yang") 影響到了克隆對象 s2,所以整個學生對象的克隆是淺層克隆。想要實現深層克隆,做以下修改
public class Teacher implements Cloneable{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Object clone(){ try { return super.clone(); } catch (Exception e) { return null; } } }
public class Student implements Cloneable{ private String name; private int age; private Teacher teacher; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Teacher getTeacher() { return teacher; } public void setTeacher(Teacher teacher) { this.teacher = teacher; } @Override public String toString() { String str = "姓名:" + getName() + ",年齡:" + getAge() + ",老師:" + ((getTeacher()==null)?"未知":getTeacher().getName()); return str; } public Object clone(){ try { Student stu = (Student) super.clone(); stu.setTeacher((Teacher)stu.getTeacher().clone()); return stu; } catch (Exception e) { return null; } } }
輸出為:
姓名:Li,年齡:10,老師:Wu 姓名:Li,年齡:10,老師:Wu 姓名:Li,年齡:10,老師:Wu
按照上面的深層克隆方法,如果類的結構不同,clone() 代碼邏輯就不同,而且還可能涉及到大量的遍歷和判斷等復雜的操作。
嫌麻煩? 試試用序列化做深層拷貝吧。將對象進行序列化後再進行反序列化,其效果相當於克隆對象。
下面改改代碼來證明這句話:
public class Student implements Serializable{ private String name; private int age; private Teacher teacher; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Teacher getTeacher() { return teacher; } public void setTeacher(Teacher teacher) { this.teacher = teacher; } @Override public String toString() { String str = "姓名:" + getName() + ",年齡:" + getAge() + ",老師:" + ((getTeacher()==null)?"未知":getTeacher().getName()); return str; } }
public class Teacher implements Serializable{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
public class CloneTest1 { public static void main(String[] args) throws Exception{ Student s1 = new Student(); s1.setAge(10); s1.setName("Li"); Teacher teacher = new Teacher(); teacher.setName("Wu"); s1.setTeacher(teacher); System.out.println(s1.toString()); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objOutputStream = new ObjectOutputStream(byteArrayOutputStream); objOutputStream.writeObject(s1); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); ObjectInputStream objInputStream = new ObjectInputStream(byteArrayInputStream); Student s2 = (Student) objInputStream.readObject(); System.out.println(s2.toString()); s1.setAge(20); s1.setName("Hu"); teacher.setName("Yang"); System.out.println(s2.toString()); } }
輸出:
姓名:Li,年齡:10,老師:Wu 姓名:Li,年齡:10,老師:Wu 姓名:Li,年齡:10,老師:Wu
幾行序列化和反序列化代碼,簡單粗暴,適合絕大多數情況,再也不用為復雜的克隆邏輯而擔憂了。