1 public class Object {2 protected native Object clone() throws CloneNotSupportedException;3 }由源代碼我們會(huì )發(fā)現:
第一:Object類(lèi)的clone()方法是一個(gè)native方法,native方法的效率一般來(lái)說(shuō)都是遠高于Java中的非native方法。這也解釋了為什么要用Object中clone()方法而不是先new一個(gè)類(lèi),然后把原始對象中的信息復制到新對象中,雖然這也實(shí)現了clone功能。(JNI是Java Native Interface的 縮寫(xiě)。從Java 1.1開(kāi)始,Java Native Interface (JNI)標準成為java平臺的一部分,它允許Java代碼和其他語(yǔ)言寫(xiě)的代碼進(jìn)行交互。JNI一開(kāi)始是為了本地已編譯語(yǔ)言,尤其是C和C++而設計的,但是它并不妨礙你使用其他語(yǔ)言,只要調用約定受支持就可以了。使用java與本地已編譯的代碼交互,通常會(huì )喪失平臺可移植性。但是,有些情況下這樣做是可以接受的,甚至是必須的,比如,使用一些舊的庫,與硬件、操作系統進(jìn)行交互,或者為了提高程序的性能。JNI標準至少保證本地代碼能工作在任何Java 虛擬機實(shí)現下。)
第二:Object類(lèi)中的 clone()方法被protected修飾符修飾。這也意味著(zhù)如果要應用 clone()方 法,必須繼承Object類(lèi),在 Java中所有的類(lèi)是缺省繼承 Object類(lèi)的,也就不用關(guān)心這點(diǎn)了。然后重載 clone()方法。還有一點(diǎn)要考慮的是為了讓其它類(lèi)能調用這個(gè) clone類(lèi)的 clone()方法,重載之后要把 clone()方法的屬性設置為 public。
第三:Object.clone()方法返回一個(gè)Object對象。我們必須進(jìn)行強制類(lèi)型轉換才能得到我們需要的類(lèi)型。
淺層復制與深層復制概念:
淺層復制: 被復制的對象的所有成員屬性都有與原來(lái)的對象相同的值,而所有的對其他對象的引用仍然指向原來(lái)的對象。換言之,淺層復制僅僅復制所考慮的對象,而不復制它所引用的對象。(概念不好理解,請結合下文的示例去理解)
深層復制:被復制對象的所有變量都含有與原來(lái)的對象相同的值,除去那些引用其他對象的變量。那些引用其他對象的變量將指向被復制過(guò)的新對象,而不是原有的那些被引用的對象。換言之,深層復制要復制的對象引用的對象都復制一遍。
Java中對象的克隆
1)在派生類(lèi)中實(shí)現Cloneable借口。
2)為了獲取對象的一份拷貝,我們可以利用Object類(lèi)的clone方法。
3)在派生類(lèi)中覆蓋積累的clone方法,聲明為public。
4)在派生類(lèi)的clone方法中,調用super.clone()。
實(shí)現Cloneable接口
首先,看一下源碼:
1 public interface Cloneable { 2 }
我們奇怪的發(fā)現Cloneable竟然是空的,那么我們?yōu)槭裁匆獙?shí)現Cloneable接口呢?其實(shí)Cloneable接口僅僅是一個(gè)標志,而且這個(gè)標志也僅僅是針對 Object類(lèi)中 clone()方法的,如果 clone 類(lèi)沒(méi)有實(shí)現 Cloneable 接口,并調用了 Object 的 clone() 方法(也就是調用了 super.Clone() 方法),那么Object 的 clone() 方法就會(huì )拋出 CloneNotSupportedException 異常。
程序示例分析:
1 public class Person { 2 private String name; 3 private int age; 4 public Person(){} 5 public Person(String name,int age){ 6 this.name=name; 7 this.age=age; 8 } 9 public Object clone(){10 Object o=null;11 try {12 o=super.clone();13 } catch (CloneNotSupportedException e) {14 e.printStackTrace();15 }16 return o;17 }18 public String getName() {19 return name;20 }21 public void setName(String name) {22 this.name = name;23 }24 public int getAge() {25 return age;26 }27 public void setAge(int age) {28 this.age = age;29 }30 }
1 public class PersonTest { 2 public static void main(String[] args) { 3 Person p1=new Person("zhangsan",18); 4 Person p2=(Person)p1.clone(); 5 p2.setName("lis"); 6 p2.setAge(20); 7 System.out.println("name=" 8 +p1.getName()+",age="+p1.getAge()); 9 //修改p2后,沒(méi)有對p1產(chǎn)生影響。10 }11 }
說(shuō)明:
1)為什么我們在派生類(lèi)中覆蓋Object的clone()方法時(shí),一定要調用super.clone()呢?在運行時(shí)刻,Object中的clone()識別你要復制的是哪一個(gè)對象,然后為此對象分配空間,并進(jìn)行對象的復制,將原始對象的內容一一復制到新對象的存儲空間中。
2)繼承自java.lang.Object.clone()方法是淺層復制。一下代碼可以證明之:
1 public class Student implements Cloneable { 2 private String name; 3 private int age; 4 private Professor pro; 5 public Student(){} 6 public Student(String name,int age,Professor pro){ 7 this.name=name; 8 this.age=age; 9 this.pro=pro;10 }11 public Object clone(){12 Object o=null;13 try {14 //Object中的clone()識別出你要復制的是哪一個(gè)對象。15 o=super.clone();16 } catch (CloneNotSupportedException e) {17 System.out.println(e.toString());18 }19 return o;20 }21 public String getName() {22 return name;23 }24 public void setName(String name) {25 this.name = name;26 }27 public int getAge() {28 return age;29 }30 public void setAge(int age) {31 this.age = age;32 }33 public Professor getPro() {34 return pro;35 }36 public void setPro(Professor pro) {37 this.pro = pro;38 }39 }40 class Professor{41 private String name;42 private int age;43 public Professor(){}44 public Professor(String name,int age){45 this.name=name;46 this.age=age;47 }48 public String getName() {49 return name;50 }51 public void setName(String name) {52 this.name = name;53 }54 public int getAge() {55 return age;56 }57 public void setAge(int age) {58 this.age = age;59 }60 }
1 public class StudentTest { 2 public static void main(String[] args) { 3 Professor p=new Professor("wangwu",50); 4 Student s1=new Student("zhangsan",18,p); 5 Student s2=(Student)s1.clone(); 6 s2.getPro().setName("maer"); 7 s2.getPro().setAge(40); 8 System.out.println("name="+s1.getPro().getName() 9 +",age="+s1.getPro().getAge());10 //name=maer,age=4011 }12 }
那么我們如何實(shí)現深層復制的克隆,即在修改s2.Professor時(shí)不影響s1.Professor?代碼改進(jìn)如下:
1 public class Student implements Cloneable { 2 private String name; 3 private int age; 4 Professor pro; 5 public Student(){} 6 public Student(String name,int age,Professor pro){ 7 this.name=name; 8 this.age=age; 9 this.pro=pro;10 }11 public Object clone(){12 Student o=null;13 try {14 //Object中的clone()識別出你要復制的是哪一個(gè)對象。15 o=(Student)super.clone();16 } catch (CloneNotSupportedException e) {17 System.out.println(e.toString());18 }19 o.pro=(Professor)pro.clone();20 return o;21 }22 public String getName() {23 return name;24 }25 public void setName(String name) {26 this.name = name;27 }28 public int getAge() {29 return age;30 }31 public void setAge(int age) {32 this.age = age;33 }34 public Professor getPro() {35 return pro;36 }37 public void setPro(Professor pro) {38 this.pro = pro;39 }40 }41 class Professor implements Cloneable{42 private String name;43 private int age;44 public Professor(){}45 public Professor(String name,int age){46 this.name=name;47 this.age=age;48 }49 public Object clone(){50 Object o=null;51 try {52 o=super.clone();53 } catch (CloneNotSupportedException e) {54 e.printStackTrace();55 }56 return o;57 }58 public String getName() {59 return name;60 }61 public void setName(String name) {62 this.name = name;63 }64 public int getAge() {65 return age;66 }67 public void setAge(int age) {68 this.age = age;69 }70 }
public class StudentTest { 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.getPro().setName("maer"); s2.getPro().setAge(40); System.out.println("name="+s1.getPro().getName() +",age="+s1.getPro().getAge()); //name=wangwu,age=50 }}
利用串行化來(lái)實(shí)現深層復制
把對象寫(xiě)到流中的過(guò)程是串行化(Serilization)過(guò)程,而把對象從流中讀出來(lái)是并行化(Deserialization)過(guò)程。應當指出的是,寫(xiě)在流中的是對象的一個(gè)拷貝,而原來(lái)對象仍然存在JVM里面。
在Java語(yǔ)言里深層復制一個(gè)對象,常??梢韵仁箤ο髮?shí)現Serializable接口,然后把對象(實(shí)際上只是對象的一個(gè)拷貝)寫(xiě)到一個(gè)流中,再從流中讀出來(lái),便可以重建對象。
這樣做的前提是對象以及對象內部所有引用到的對象都是可串行化的,否則,就需要仔細考察那些不可串行化的對象是否設成transient,從而將之排除在復制過(guò)程之外。代碼改進(jìn)如下:
1 public class Student implements Serializable { 2 private String name; 3 private int age; 4 Professor pro; 5 public Student(){} 6 public Student(String name,int age,Professor pro){ 7 this.name=name; 8 this.age=age; 9 this.pro=pro;10 }11 public Object deepClone() throws IOException, ClassNotFoundException{12 //將對象寫(xiě)到流中13 ByteArrayOutputStream bo=new ByteArrayOutputStream();14 ObjectOutputStream oo=new ObjectOutputStream(bo);15 oo.writeObject(this);16 //從流中讀出來(lái)17 ByteArrayInputStream bi=new ByteArrayInputStream(bo.toByteArray());18 ObjectInputStream oi=new ObjectInputStream(bi);19 return oi.readObject();20 }21 public String getName() {22 return name;23 }24 public void setName(String name) {25 this.name = name;26 }27 public int getAge() {28 return age;29 }30 public void setAge(int age) {31 this.age = age;32 }33 public Professor getPro() {34 return pro;35 }36 public void setPro(Professor pro) {37 this.pro = pro;38 }39 }40 class Professor implements Serializable{41 private String name;42 private int age;43 public Professor(){}44 public Professor(String name,int age){45 this.name=name;46 this.age=age;47 }48 public String getName() {49 return name;50 }51 public void setName(String name) {52 this.name = name;53 }54 public int getAge() {55 return age;56 }57 public void setAge(int age) {58 this.age = age;59 }60 }
1 public class StudentTest { 2 public static void main(String[] args) throws IOException, ClassNotFoundException { 3 Professor p=new Professor("wangwu",50); 4 Student s1=new Student("zhangsan",18,p); 5 Student s2=(Student)s1.deepClone(); 6 s2.getPro().setName("maer"); 7 s2.getPro().setAge(40); 8 System.out.println("name="+s1.getPro().getName() 9 +",age="+s1.getPro().getAge());10 //name=wangwu,age=5011 }12 }
聯(lián)系客服