第11條:謹慎地覆蓋clone

Cloneable接口代表這樣的對象時容許克隆的,但這個接口並無成功達到這個目的,主要是由於它缺乏一個clone方法,Object的clone方法是受保護的。若是不借助反射,就不能僅僅由於一個對象實現了Colneable就能夠釣魚clone方法,即便是反射調用也不能保證這個對象必定具備可訪問clone方法。數據結構

既然Cloneable並無包含任何方法,那麼它到底有什麼用呢?它其實以爲了Object中受保護的clone方法實現的行爲,若是一個類實現了Cloneable那麼Object的clone方法就返回該對象的逐域拷貝,不然會拋出CloneNotSupportedException。但真說接口一種極端非典型用法,不值得提倡。this

若是實現Cloneable接口是要對某個類起到做用,類和它的全部超類都必須遵照一個必定協議,言外之意就是無需調用構造器就能夠建立對象。spa

Clone它的通用約定很是弱:code

  建立和返回該對象的一個拷貝。這個拷貝的精確含義取決於該對象的類。通常含義是,對於任何對象x,表達式x.clone() != x 將會是true,而且,表達式x.clone().getClass() == x.getClass() 將會是true,但這些不是絕對的要求,一般狀況下,表達式x.clone().equals(x) 將會是true,這也不是一個絕對的要求,拷貝對象每每是建立它的類的一個新實例,但它同時也會要求拷貝內部的數據結構。對象

下面咱們看下一個例子:blog

public class Student implements Cloneable{
    String name;
    int age;
    
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }
    
    public Object clone(){
        Object o = null;
        try{
             o = (Student)super.clone();//Object 中的clone()識別出你要複製的是哪一個對象    
        }catch(CloneNotSupportedException e){
             System.out.println(e.toString()); 
        }
        return o; 
    }

    public static void main(String[] args){ 
            Student s1=new Student("zhangsan",18); 
            Student s2=(Student)s1.clone(); 
            System.out.println("克隆後s2:name="+s2.name+","+"age="+s2.age); 
            s2.name="lisi"; 
            s2.age=20; 
            //修改學生2後,不影響學生1的值。
            System.out.println("克隆修改後s1:name="+s1.name+","+"age="+s1.age); 
            System.out.println("克隆修改後s2:name="+s2.name+","+"age="+s2.age);
        }
}

這時候,若是類的每一個域包含一個基本類型的值,或者包含一個指向不可變對象的引用,那麼被返回的對象則正是所須要的對象,只須要簡單地調用super.clone() 而不用作進一步的處理。可是!若是對象中其餘對象的引用時,那麼只是簡單的clone就沒法作到徹底的克隆了,下面的例子咱們就能夠體會到接口

class Professor { 
    String name; 
    int age; 
    Professor(String name,int age){ 
        this.name=name; 
        this.age=age; 
    } 
} 

public class Student implements Cloneable{ 
    String name;// 常量對象。 
    int age; 
    Professor p;// 學生1和學生2的引用值都是同樣的。 

    Student(String name,int age,Professor p){ 
        this.name=name; 
        this.age=age; 
        this.p=p; 
    } 

    public Object clone(){ 
        Student o=null; 
        try{ 
                o=(Student)super.clone(); 
        }catch(CloneNotSupportedException e){ 
                System.out.println(e.toString()); 
        } 
        
        return o; 
    } 

    public static void main(String[] args){ 
          Professor p=new Professor("wangwu",50); 
          Student s1=new Student("zhangsan",18,p); 
          Student s2=(Student)s1.clone(); 
          System.out.println("克隆後s1:name="+s1.p.name+","+"age="+s1.p.age);
          System.out.println("克隆後s2:name="+s2.p.name+","+"age="+s2.p.age);
          s2.p.name="lisi"; 
          s2.p.age=30;  
          System.out.println("克隆後s1:name="+s1.p.name+","+"age="+s1.p.age);
          System.out.println("克隆後s2:name="+s2.p.name+","+"age="+s2.p.age);
    } 
}

從結果上咱們能夠看出,s2對s1進行克隆時,對s1的屬性Professor p並無進行克隆,致使s1和s2對其引用指向同一個,這會形成s2若改變了值,s1則也被動改變了。那應該如何實現深層次的克隆,即修改s2的教授不會影響s1的教授?其實很簡單,只須要對Professor進行修改,以下所示便可get

class Professor  implements Cloneable{ 
            String name; 
            int age; 
            Professor(String name,int age){ 
                this.name=name; 
                this.age=age; 
            } 
    
            public Object clone(){
                Object o = null;
                try{ 
                    o = super.clone(); 
                }catch(CloneNotSupportedException e){ 
                    System.out.println(e.toString()); 
                } 
                return o; 
            }
        }

修改Professor後,還須要在Student的clone方法中加入一句代碼:o.p=(Professor)p.clone(); io

public Object clone(){ 
        Student o=null; 
        try{ 
                o=(Student)super.clone(); 
        }catch(CloneNotSupportedException e){ 
                System.out.println(e.toString()); 
        } 
       o.p=(Professor)p.clone(); 
        return o; 
} 

看到結果就如咱們所但願的那樣。所以,在使用clone時,必定要分清須要克隆的對象屬性。class

相關文章
相關標籤/搜索