java.lang.Object.clone()

 首先,看一下源碼:

1 public class Object  {
2     protected native Object clone() throws CloneNotSupportedException;
3 }

  由源代碼我們會發現:

  第一:Object類的clone()方法是一個native方法,native方法的效率一般來說都是遠高於Java中的非native方法。這也解釋了爲什麼要用Object中clone()方法而不是先new一個類,然後把原始對象中的信息複製到新對象中,雖然這也實現了clone功能。(JNI是Java Native Interface的 縮寫。從Java 1.1開始,Java Native Interface (JNI)標準成爲java平臺的一部分,它允許Java代碼和其他語言寫的代碼進行交互。JNI一開始是爲了本地已編譯語言,尤其是C和C++而設計的,但是它並不妨礙你使用其他語言,只要調用約定受支持就可以了。使用java與本地已編譯的代碼交互,通常會喪失平臺可移植性。但是,有些情況下這樣做是可以接受的,甚至是必須的,比如,使用一些舊的庫,與硬件、操作系統進行交互,或者爲了提高程序的性能。JNI標準至少保證本地代碼能工作在任何Java 虛擬機實現下。

  第二:Object類中的 clone()方法被protected修飾符修飾。這也意味着如果要應用 clone()方 法,必須繼承Object類,在 Java中所有的類是缺省繼承 Object類的,也就不用關心這點了。然後重載 clone()方法。還有一點要考慮的是爲了讓其它類能調用這個 clone類的 clone()方法,重載之後要把 clone()方法的屬性設置爲 public。

  第三:Object.clone()方法返回一個Object對象。我們必須進行強制類型轉換才能得到我們需要的類型。

 

  淺層複製與深層複製概念:

  淺層複製: 被複制的對象的所有成員屬性都有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象。換言之,淺層複製僅僅複製所考慮的對象,而不復制它所引用的對象。(概念不好理解,請結合下文的示例去理解)

  深層複製:被複制對象的所有變量都含有與原來的對象相同的值,除去那些引用其他對象的變量。那些引用其他對象的變量將指向被複制過的新對象,而不是原有的那些被引用的對象。換言之,深層複製要複製的對象引用的對象都複製一遍。

 

  Java中對象的克隆

  1)在派生類中實現Cloneable藉口。

  2)爲了獲取對象的一份拷貝,我們可以利用Object類的clone方法。

  3)在派生類中覆蓋積累的clone方法,聲明爲public。

  4)在派生類的clone方法中,調用super.clone()。

 

  實現Cloneable接口 

  首先,看一下源碼:  

1 public interface Cloneable { 
2 }

  我們奇怪的發現Cloneable竟然是空的,那麼我們爲什麼要實現Cloneable接口呢?其實Cloneable接口僅僅是一個標誌,而且這個標誌也僅僅是針對 Object類中 clone()方法的,如果 clone 類沒有實現 Cloneable 接口,並調用了 Object 的 clone() 方法(也就是調用了 super.Clone() 方法),那麼Object 的 clone() 方法就會拋出 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後,沒有對p1產生影響。
10     }
11 }
複製代碼

  說明:

  1)爲什麼我們在派生類中覆蓋Object的clone()方法時,一定要調用super.clone()呢?在運行時刻,Object中的clone()識別你要複製的是哪一個對象,然後爲此對象分配空間,並進行對象的複製,將原始對象的內容一一複製到新對象的存儲空間中。

  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()識別出你要複製的是哪一個對象。
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=40
11     }
12 }
複製代碼

  那麼我們如何實現深層複製的克隆,即在修改s2.Professor時不影響s1.Professor?代碼改進如下:

複製代碼
 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()識別出你要複製的是哪一個對象。
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
    }
}

import java.io.*;
class Thing1 implements Serializable {}
class Thing2 implements Serializable {
Thing1 o1 = new Thing1();
}
class Thing3 implements Cloneable {
public Object clone() {
Object o = null;
try{
o = super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("Thing3 can't clone");
}
return o;
}
}
class Thing4 implements Cloneable {
Thing3 o3 = new Thing3();
public Object clone() {
Thing4 o = null;
try {
o = (Thing4)super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("Thing4 can't clone");
}
// Clone the field, too:
o.o3 = (Thing3)o3.clone();
return o;
}
}
public class Compete {
static final int SIZE = 5000;
public static void main(String[] args) {
    Thing2[] a = new Thing2[SIZE];
for(int i = 0; i < a.length; i++)
a[i] = new Thing2();
Thing4[] b = new Thing4[SIZE];
for(int i = 0; i < b.length; i++)
b[i] = new Thing4();
try {
long t1 = System.currentTimeMillis();
ByteArrayOutputStream buf = new ByteArrayOutputStream();
ObjectOutputStream o = new ObjectOutputStream(buf);
for(int i = 0; i < a.length; i++) 
     o.writeObject(a[i]);
// Now get copies:
ObjectInputStream in =
new ObjectInputStream(
new ByteArrayInputStream(buf.toByteArray()));
Thing2[] c = new Thing2[SIZE];
for(int i = 0; i < c.length; i++)
     c[i] = (Thing2)in.readObject();
 long t2 = System.currentTimeMillis();
System.out.println("Duplication via serialization: " +(t2 -t1) + " Milliseconds");
// Now try cloning:
   t1 = System.currentTimeMillis();
   Thing4[] d = new Thing4[SIZE];
for(int i = 0; i < d.length; i++)
       d[i] = (Thing4)b[i].clone();
t2 = System.currentTimeMillis();
System.out.println("Duplication via cloning: " +(t2 -t1) + " Milliseconds");
} catch(Exception e) {
   e.printStackTrace();
}
}
} ///:~

其中,Thing2 和 Thing4 包含了成員對象,所以需要進行一些深層複製。一個有趣的地方是儘管
Serializable 類很容易設置,但在複製它們時卻要做多得多的工作。克隆涉及到大量的類設置工作,但實際
的對象複製是相當簡單的。結果很好地說明了一切。下面是幾次運行分別得到的結果:
的確
Duplication via serialization: 3400 Milliseconds
Duplication via cloning: 110 Milliseconds
Duplication via serialization: 3410 Milliseconds
Duplication via cloning: 110 Milliseconds
Duplication via serialization: 3520 Milliseconds
Duplication via cloning: 110 Milliseconds
除了序列化和克隆之間巨大的時間差異以外,我們也注意到序列化技術的運行結果並不穩定,而克隆每一次
花費的時間都是相同的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章