java Object對象的clone方法

參考copy連接:http://blog.csdn.net/bigconvience/article/details/25025561java

在看原型模式,發現要用到clone這個方法,之前和朋友聊過,沒怎麼看過,恰好要用,就看看了。express

源碼解釋:數組

    /**
     * Creates and returns a copy of this object.  The precise meaning
     * of "copy" may depend on the class of the object. The general
     * intent is that, for any object {@code x}, the expression:
     * <blockquote>
     * <pre>
     * x.clone() != x</pre></blockquote>
     * will be true, and that the expression:
     * <blockquote>
     * <pre>
     * x.clone().getClass() == x.getClass()</pre></blockquote>
     * will be {@code true}, but these are not absolute requirements.
     * While it is typically the case that:
     * <blockquote>
     * <pre>
     * x.clone().equals(x)</pre></blockquote>
     * will be {@code true}, this is not an absolute requirement.
     * <p>
     * By convention, the returned object should be obtained by calling
     * {@code super.clone}.  If a class and all of its superclasses (except
     * {@code Object}) obey this convention, it will be the case that
     * {@code x.clone().getClass() == x.getClass()}.
     * <p>
     * By convention, the object returned by this method should be independent
     * of this object (which is being cloned).  To achieve this independence,
     * it may be necessary to modify one or more fields of the object returned
     * by {@code super.clone} before returning it.  Typically, this means
     * copying any mutable objects that comprise the internal "deep structure"
     * of the object being cloned and replacing the references to these
     * objects with references to the copies.  If a class contains only
     * primitive fields or references to immutable objects, then it is usually
     * the case that no fields in the object returned by {@code super.clone}
     * need to be modified.
     * <p>
     * The method {@code clone} for class {@code Object} performs a
     * specific cloning operation. First, if the class of this object does
     * not implement the interface {@code Cloneable}, then a
     * {@code CloneNotSupportedException} is thrown. Note that all arrays
     * are considered to implement the interface {@code Cloneable} and that
     * the return type of the {@code clone} method of an array type {@code T[]}
     * is {@code T[]} where T is any reference or primitive type.
     * Otherwise, this method creates a new instance of the class of this
     * object and initializes all its fields with exactly the contents of
     * the corresponding fields of this object, as if by assignment; the
     * contents of the fields are not themselves cloned. Thus, this method
     * performs a "shallow copy" of this object, not a "deep copy" operation.
     * <p>
     * The class {@code Object} does not itself implement the interface
     * {@code Cloneable}, so calling the {@code clone} method on an object
     * whose class is {@code Object} will result in throwing an
     * exception at run time.
     *
     * @return     a clone of this instance.
     * @throws  CloneNotSupportedException  if the object's class does not
     *               support the {@code Cloneable} interface. Subclasses
     *               that override the {@code clone} method can also
     *               throw this exception to indicate that an instance cannot
     *               be cloned.
     * @see java.lang.Cloneable
     */
    protected native Object clone() throws CloneNotSupportedException;

一大串英文我看不怎麼懂,都是看別人的博客和翻譯文檔的。ide

中文jdk文檔:測試

clone

protected Object clone()
                throws CloneNotSupportedException
建立並返回此對象的一個副本。「副本」的準確含義可能依賴於對象的類。這樣作的目的是,對於任何對象 x,表達式:
x.clone() != x
爲 true,表達式:
x.clone().getClass() == x.getClass()
也爲 true,但這些並不是必需要知足的要求。通常狀況下:
x.clone().equals(x)
爲 true,但這並不是必需要知足的要求。
按照慣例,返回的對象應該經過調用 super.clone 得到。若是一個類及其全部的超類(Object 除外)都遵照此約定,則 x.clone().getClass() == x.getClass()。

按照慣例,此方法返回的對象應該獨立於該對象(正被複制的對象)。要得到此獨立性,在 super.clone 返回對象以前,有必要對該對象的一個或多個字段進行修改。這一般意味着要複製包含正在被複制對象的內部「深層結構」的全部可變對象,並使用對副本的引用替換對這些對象的引用。若是一個類只包含基本字段或對不變對象的引用,那麼一般不須要修改 super.clone 返回的對象中的字段。

Object 類的 clone 方法執行特定的複製操做。首先,若是此對象的類不能實現接口 Cloneable,則會拋出 CloneNotSupportedException。注意,全部的數組都被視爲實現接口 Cloneable。不然,此方法會建立此對象的類的一個新實例,並像經過分配那樣,嚴格使用此對象相應字段的內容初始化該對象的全部字段;這些字段的內容沒有被自我複製。因此,此方法執行的是該對象的「淺表複製」,而不「深層複製」操做。

Object 類自己不實現接口 Cloneable,因此在類爲 Object 的對象上調用 clone 方法將會致使在運行時拋出異常。

返回:
此實例的一個副本。
拋出:
CloneNotSupportedException - 若是對象的類不支持 Cloneable 接口,則重寫 clone 方法的子類也會拋出此異常,以指示沒法複製某個實例。
另請參見:
Cloneable

 

cloneable接口的文檔:ui

public interface Cloneable
此類實現了 Cloneable 接口,以指示 Object.clone() 方法能夠合法地對該類實例進行按字段複製。

若是在沒有實現 Cloneable 接口的實例上調用 Object 的 clone 方法,則會致使拋出 CloneNotSupportedException 異常。

按照慣例,實現此接口的類應該使用公共方法重寫 Object.clone(它是受保護的)。請參閱 Object.clone(),以得到有關重寫此方法的詳細信息。

注意,此接口不 包含 clone 方法。所以,由於某個對象實現了此接口就克隆它是不可能的。即便 clone 方法是反射性調用的,也沒法保證它將得到成功。

從如下版本開始:
JDK1.0
另請參見:
CloneNotSupportedException, Object.clone()

 

如今能夠知道的是,clone方法就是返回一個原對象的拷貝,默認走的是淺拷貝。克隆的目的是複製對象,可是新的對象是獨立於原來的對象的,通常咱們克隆出來的對象都在一些屬性作了更改,這個時候須要當心一點,若是更改的屬性是引用數據類型,可能會影響到原來的對象,若是都是基本數據類型則不怕。使用clone方法的前提是繼承Cloneable接口,數組默認實現了Cloneable接口,默認走的是淺拷貝。this

 

那麼問題來了,什麼是淺拷貝?什麼是深拷貝呢?spa

2.淺克隆(shadow clone).net

   克隆就是複製一個對象的複本.若只須要複製對象的字段值(對於基本數據類型,如:int,long,float等,則複製值;對於複合數據類型僅複製該字段值,如數組變量則複製地址,對於對象變量則複製對象的reference。翻譯

例子:

 

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. public class ShadowClone implements Cloneable{  
  2.          
  3.     private int a;   // 基本類型  
  4.     private int[] b; // 非基本類型  
  5.     // 重寫Object.clone()方法,並把protected改成public  
  6.     @Override  
  7.     public Object clone(){  
  8.         ShadowClone sc = null;  
  9.         try  
  10.         {  
  11.             sc = (ShadowClone) super.clone();  
  12.         } catch (CloneNotSupportedException e){  
  13.             e.printStackTrace();  
  14.         }  
  15.         return sc;  
  16.     }  
  17.     public int getA()  
  18.     {  
  19.         return a;  
  20.     }  
  21.     public void setA(int a)  
  22.     {  
  23.         this.a = a;  
  24.     }  
  25.     public int[] getB() {  
  26.     return b;  
  27.     }  
  28.     public void setB(int[] b) {  
  29.     this.b = b;  
  30.     }    
  31. }  

而後進行測試:

 

 

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. public class Test{  
  2.     public static void main(String[] args) throws CloneNotSupportedException{  
  3.         ShadowClone c1 = new ShadowClone();  
  4.         //對c1賦值  
  5.         c1.setA(100) ;  
  6.         c1.setB(new int[]{1000}) ;  
  7.           
  8.         System.out.println("克隆前c1:  a="+c1.getA()+" b="+c1.getB()[0]);  
  9.         //克隆出對象c2,並對c2的屬性A,B,C進行修改  
  10.         ShadowClone c2 = (ShadowClone) c1.clone();  
  11.         //對c2進行修改  
  12.         c2.setA(50) ;  
  13.         int []a = c2.getB() ;  
  14.         a[0]=5 ;  
  15.         c2.setB(a);  
  16.         System.out.println("克隆前c1:  a="+c1.getA()+" b="+c1.getB()[0]);  
  17.         System.out.println("克隆後c2:  a="+c2.getA()+ " b[0]="+c2.getB()[0]);  
  18.     }  
  19. }  

結果爲:

 

 

克隆前c1:  a=100 b=1000
克隆前c1:  a=100 b=5
克隆後c2:  a=50 b[0]=5


c1和c2的對象模型:

                                                                     

能夠看出,基本類型可使用淺克隆,而對於引用類型,因爲引用的是內容相同,因此改變c2實例對象中的屬性就會影響到c1。因此引用類型須要使用深克隆。另外,在開發一個不可變類的時候,若是這個不可變類中成員有引用類型,則就須要經過深克隆來達到不可變的目的。

3.深克隆(deep clone)

    深克隆與淺克隆的區別在於對複合數據類型的複製。若對象中的某個字段爲複合類型,在克隆對象的時候,須要爲該字段從新建立一個對象。

例子:

 

[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1. public class DeepClone implements Cloneable {  
  2.   
  3.     private int a;   // 基本類型  
  4.     private int[] b; // 非基本類型  
  5.     // 重寫Object.clone()方法,並把protected改成public  
  6.     @Override  
  7.     public Object clone(){  
  8.         DeepClone sc = null;  
  9.         try  
  10.         {  
  11.             sc = (DeepClone) super.clone();  
  12.             int[] t = sc.getB();  
  13.             int[] b1 = new int[t.length];  
  14.             for (int i = 0; i < b1.length; i++) {  
  15.                 b1[i] = t[i];  
  16.             }  
  17.             sc.setB(b1);  
  18.         } catch (CloneNotSupportedException e){  
  19.             e.printStackTrace();  
  20.         }  
  21.         return sc;  
  22.     }  
  23.     public int getA()  
  24.     {  
  25.         return a;  
  26.     }  
  27.     public void setA(int a)  
  28.     {  
  29.         this.a = a;  
  30.     }  
  31.     public int[] getB() {  
  32.         return b;  
  33.     }  
  34.     public void setB(int[] b) {  
  35.         this.b = b;  
  36.     }  
  37. }  
[java]  view plain  copy
 
 在CODE上查看代碼片派生到個人代碼片
  1.   

結果爲:

克隆前c1:  a=100 b=1000

克隆前c1:  a=100 b=1000
克隆後c2:  a=50 b[0]=5

 

對象模型:

                                        

 

上面是copy的,本身敲一敲:

兩個準備的類:
class
Person implements Cloneable{ @Override protected Person clone() throws CloneNotSupportedException { return (Person) super.clone(); } String name; int age; Job job; 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 Job getJob() { return job; } public void setJob(Job job) { this.job = job; } @Override public String toString(){ return "name: " + name + ",age: " + age + ",job: " + job; } } class Job{ String jobName; String address; public String getJobName() { return jobName; } public void setJobName(String jobName) { this.jobName = jobName; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }

public class CloneTest {
@Test
public void shaowClone() throws Exception{
Person p1 = new Person();
p1.setName("guo");
p1.setAge(22);
Job job = new Job();
job.setJobName("IT");
job.setAddress("shanghai");
p1.setJob(job);

Person p2 = p1.clone();
System.out.println(p1.toString());
System.out.println(p2.toString());

p2.getJob().setJobName("programmer");
System.out.println(p1.getJob().getJobName());
}

  獲得結果:
    name: guo,age: 22,job: test.Job@4f2410ac
    name: guo,age: 22,job: test.Job@4f2410ac
    programmer

淺拷貝,就是Object默認的clone方法,徹底的copy了這個類,基本數據類型copy了值,引用數據類型copy的是對象的引用,因此若是要對對象進行修改,可使用深拷貝。
}

所謂的深拷貝,就是本身重寫了一下clone方法,將引用變量變成值傳遞而不是引用傳遞。
修改的代碼;
 @Override
    protected Person clone() throws CloneNotSupportedException {
        Person ectype = (Person) super.clone();
        Job tmp = new Job();
        tmp.setJobName(ectype.getJob().getJobName());
        tmp.setAddress(ectype.getJob().getAddress());
        ectype.setJob(tmp);
        return ectype;
    }
測試代碼:
@Test
public void shaowClone() throws Exception{
Person p1 = new Person();
p1.setName("guo");
p1.setAge(22);
Job job = new Job();
job.setJobName("IT");
job.setAddress("shanghai");
p1.setJob(job);

Person p2 = p1.clone();
System.out.println(p1.toString());
System.out.println(p2.toString());

p2.getJob().setJobName("programmer");
System.out.println(p2.getJob().getJobName());
System.out.println(p1.getJob().getJobName());
}

結果:

 

 
  
 

 

 

 

 

 

 

 over

相關文章
相關標籤/搜索