第11條:謹慎地覆蓋clone

clone 方法使用詳解

clone顧名思義就是復制, 在Java語言中, clone方法被對象調(diào)用义黎,所以會復制對象禾进。所謂的復制對象豁跑,首先要分配一個和源對象同樣大小的空間,在這個空間中創(chuàng)建一個新的對象泻云。java創(chuàng)建對象的方式:
1 使用new操作符創(chuàng)建一個對象
2 使用clone方法復制一個對象
那么這兩種方式有什么相同和不同呢艇拍? new操作符的本意是分配內(nèi)存。程序執(zhí)行到new操作符時宠纯, 首先去看new操作符后面的類型卸夕,因為知道了類型,才能知道要分配多大的內(nèi)存空間婆瓜。分配完內(nèi)存之后快集,再調(diào)用構(gòu)造函數(shù),填充對象的各個域廉白,這一步叫做對象的初始化个初,構(gòu)造方法返回后,一個對象創(chuàng)建完畢猴蹂,可以把他的引用(地址)發(fā)布到外部院溺,在外部就可以使用這個引用操縱這個對象。而clone在第一步是和new相似的磅轻, 都是分配內(nèi)存珍逸,調(diào)用clone方法時,分配的內(nèi)存和源對象(即調(diào)用clone方法的對象)相同聋溜,然后再使用原對象中對應的各個域谆膳,填充新對象的域, 填充完成之后撮躁,clone方法返回摹量,一個新的相同的對象被創(chuàng)建,同樣可以把這個新對象的引用發(fā)布到外部。

復制對象 or 復制引用

public class fz {
    public static void main(String[] args) {
        Person p = new Person(23, "zhang");  
        Person p1 = p;  
          
        System.out.println(p);  
        System.out.println(p1);  
    }
}
復制引用.png

當Person p1 = p;執(zhí)行之后缨称,通過打印結(jié)果可以看出凝果,打印的地址值是相同的,既然地址值都是相同的睦尽,那么肯定是同一個對象器净。p和p1只是引用而已,他們都指向了一個相同的對象Person(23当凡,“zhang”)山害。可以把這種現(xiàn)場叫做引用的復制沿量。上面代碼執(zhí)行完成之后浪慌, 內(nèi)存中的情景如下圖所示:

Paste_Image.png

而下面的代碼是真真正正的克隆了一個對象。

public class fz1 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person p = new Person(23, "zhang");  
        Person p1 = (Person) p.clone();  
          
        System.out.println(p);  
        System.out.println(p1);  
    }
}
Paste_Image.png

從打印結(jié)果可以看出朴则,兩個對象的地址是不同的权纤,也就是說創(chuàng)建了新的對象, 而不是把原對象的地址賦給了一個新的引用變量乌妒。以上代碼執(zhí)行完成后汹想, 內(nèi)存中的情景如下圖所示:

Paste_Image.png

深拷貝 or 淺拷貝

深拷貝:復制對象
淺拷貝:復制引用(如果只是用Object中默認的clone方法,是淺拷貝)
上面的示例代碼中撤蚊,Person中有兩個成員變量古掏,分別是name和age, name是String類型侦啸, age是int類型槽唾。代碼非常簡單,如下所示:

public class Person implements Cloneable{
    private int age ;  
    private String name;  
      
    public Person(int age, String name) {  
        this.age = age;  
        this.name = name;  
    }  
      
    public Person() {}  
  
    public int getAge() {  
        return age;  
    }  
  
    public String getName() {  
        return name;  
    }  
      
    @Override  
    protected Object clone() throws CloneNotSupportedException {  
        return (Person)super.clone();  
    } 
}

由于age是基本數(shù)據(jù)類型光涂, 那么對它的拷貝沒有什么疑議庞萍,直接將一個4字節(jié)的整數(shù)值拷貝過來就行。但是name是String類型的顶捷, 它只是一個引用挂绰, 指向一個真正的String對象,那么對它的拷貝有兩種方式: 直接將源對象中的name的引用值拷貝給新對象的name字段服赎, 或者是根據(jù)原Person對象中的name指向的字符串對象創(chuàng)建一個新的相同的字符串對象葵蒂,將這個新字符串對象的引用賦給新拷貝的Person對象的name字段。這兩種拷貝方式分別叫做淺拷貝和深拷貝重虑。

下面通過代碼進行驗證践付。如果兩個Person對象的name的地址值相同, 說明兩個對象的name都指向同一個String對象缺厉, 也就是淺拷貝永高, 而如果兩個對象的name的地址值不同隧土, 那么就說明指向不同的String對象, 也就是在拷貝Person對象的時候命爬, 同時拷貝了name引用的String對象曹傀, 也就是深拷貝。驗證代碼如下:

public class fz2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person p = new Person(23, "zhang");  
        Person p1 = (Person) p.clone();  
          
        System.out.println("p.getName().hashCode() : " + p.getName().hashCode());  
        System.out.println("p1.getName().hashCode() : " + p1.getName().hashCode());  
          
        String result = p.getName().hashCode() == p1.getName().hashCode()   
                ? "clone是淺拷貝的" : "clone是深拷貝的";  
        System.out.println(result);  
    }
}
Paste_Image.png

覆蓋Object中的clone方法饲宛, 實現(xiàn)深拷貝

現(xiàn)在為了要在clone對象時進行深拷貝皆愉, 那么就要Clonable接口,覆蓋并實現(xiàn)clone方法艇抠,除了調(diào)用父類中的clone方法得到新的對象幕庐, 還要將該類中的引用變量也clone出來。如果只是用Object中默認的clone方法家淤,是淺拷貝的异剥,再次以下面的代碼驗證:

public class fz4 {
    static class Body implements Cloneable {
        public Head head;
        public Body() {
        }
        public Body(Head head) {
            this.head = head;
        }
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    static class Head /* implements Cloneable */{
        public Face face;
        public Head() {
        }
        public Head(Face face) {
            this.face = face;
        }
    }
    
    static class Face{} 
    public static void main(String[] args) throws CloneNotSupportedException {
        Body body = new Body(new Head());
        Body body1 = (Body) body.clone();
        System.out.println("body == body1 : " + (body == body1));
        System.out.println("body.head == body1.head : " + (body.head == body1.head));
    }
}
Paste_Image.png

在以上代碼中, 有兩個主要的類絮重, 分別為Body和head冤寿, 在Body類中, 組合了一個head對象绿鸣。當對Body對象進行clone時疚沐, 它組合的head對象只進行淺拷貝暂氯。

如果要使Body對象在clone時進行深拷貝潮模, 那么就要在Body的clone方法中,將源對象引用的Head對象也clone一份痴施。

public class fz4 {
    static class Body implements Cloneable {
        public Head head;
        public Body() {
        }
        public Body(Head head) {
            this.head = head;
        }
        @Override
        protected Object clone() throws CloneNotSupportedException {
//            return super.clone();
            Body newBody =  (Body) super.clone();  
            newBody.head = (Head) head.clone();  
            return newBody;
        }
    }
    static class Head  implements Cloneable {
        public Face face;
        public Head() {
        }
        public Head(Face face) {
            this.face = face;
        }
        
        @Override  
        protected Object clone() throws CloneNotSupportedException {  
                return super.clone();  
        }  
    }
    
    static class Face{} 
    public static void main(String[] args) throws CloneNotSupportedException {
        Body body = new Body(new Head());
        Body body1 = (Body) body.clone();
        System.out.println("body == body1 : " + (body == body1));
        System.out.println("body.head == body1.head : " + (body.head == body1.head));
    }
}
Paste_Image.png

由此可見擎厢, body和body1內(nèi)的head引用指向了不同的Head對象, 也就是說在clone Body對象的同時辣吃, 也拷貝了它所引用的Head對象动遭, 進行了深拷貝。

真的是深拷貝嗎神得?

如果想要深拷貝一個對象厘惦, 這個對象必須要實現(xiàn)Cloneable接口,實現(xiàn)clone方法哩簿,并且在clone方法內(nèi)部宵蕉,把該對象引用的其他對象也要clone一份 , 這就要求這個被引用的對象必須也要實現(xiàn)Cloneable接口并且實現(xiàn)clone方法节榜。
那么羡玛,按照上面的結(jié)論, Body類組合了Head類宗苍, 而Head類組合了Face類稼稿,要想深拷貝Body類薄榛,必須在Body類的clone方法中將Head類也要拷貝一份,但是在拷貝Head類時让歼,默認執(zhí)行的是淺拷貝敞恋,也就是說Head中組合的Face對象并不會被拷貝。驗證代碼如下:

public class fz6 {
    static class Body implements Cloneable {
        public Head head;
        public Body() {
        }
        public Body(Head head) {
            this.head = head;
        }
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Body newBody = (Body) super.clone();
            newBody.head = (Head) head.clone();
            return newBody;
        }
    }
    static class Head implements Cloneable {
        public Face face;
        public Head() {
        }
        public Head(Face face) {
            this.face = face;
        }
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    static class Face {}
    public static void main(String[] args) throws CloneNotSupportedException {
        Body body = new Body(new Head(new Face()));
        Body body1 = (Body) body.clone();
        System.out.println("body == body1 : " + (body == body1));
        System.out.println("body.head == body1.head : " + (body.head == body1.head));
        System.out.println("body.head.face == body1.head.face : " + (body.head.face == body1.head.face));
    }
}
Paste_Image.png

內(nèi)存結(jié)構(gòu)圖如下圖所示:

Paste_Image.png

對于Body對象來說谋右,其實應該算是深拷貝耳舅,因為對Body對象內(nèi)所引用的其他對象(目前只有Head)都進行了拷貝,也就是說兩個獨立的Body對象內(nèi)的head引用已經(jīng)指向了獨立的兩個Head對象倚评。但是浦徊,這對于兩個Head對象來說,他們指向了同一個Face對象天梧,這就說明盔性,兩個Body對象還是有一定的聯(lián)系,并沒有完全的獨立呢岗。這應該說是一種不徹底的深拷貝冕香。

如何進行徹底的深拷貝

于上面的例子來說,怎樣才能保證兩個Body對象完全獨立呢后豫?只要在拷貝Head對象的時候悉尾,也將Face對象拷貝一份就可以了。這需要讓Face類也實現(xiàn)Cloneable接口挫酿,實現(xiàn)clone方法构眯,并且在在Head對象的clone方法中,拷貝它所引用的Face對象早龟。修改的部分代碼如下:

   static class Head implements Cloneable {
        public Face face;
        public Head() {
        }
        public Head(Face face) {
            this.face = face;
        }
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Head newHead =(Head)super.clone();
            newHead.face = (Face)face.clone();
            return newHead;
        }
    }
    static class Face implements Cloneable{
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
Paste_Image.png

這說名兩個Body已經(jīng)完全獨立了惫霸,他們間接引用的face對象已經(jīng)被拷貝,也就是引用了獨立的Face對象葱弟。
依此類推壹店,如果Face對象還引用了其他的對象, 比如說Mouth芝加,如果不經(jīng)過處理硅卢,Body對象拷貝之后還是會通過一級一級的引用,引用到同一個Mouth對象藏杖。同理将塑, 如果要讓Body在引用鏈上完全獨立, 只能顯式的讓Mouth對象也被拷貝制市。到此抬旺,可以得到如下結(jié)論:如果在拷貝一個對象時,要想讓這個拷貝的對象和源對象完全彼此獨立祥楣,那么在引用鏈上的每一級對象都要被顯式的拷貝开财。所以創(chuàng)建徹底的深拷貝是非常麻煩的汉柒,尤其是在引用關(guān)系非常復雜的情況下, 或者在引用鏈的某一級上引用了一個第三方的對象责鳍, 而這個對象沒有實現(xiàn)clone方法碾褂, 那么在它之后的所有引用的對象都是被共享的。 舉例來說历葛,如果被Head引用的Face類是第三方庫中的類正塌,并且沒有實現(xiàn)Cloneable接口,那么在Face之后的所有對象都會被拷貝前后的兩個Body對象共同引用恤溶。假設Face對象內(nèi)部組合了Mouth對象乓诽,并且Mouth對象內(nèi)部組合了Tooth對象, 內(nèi)存結(jié)構(gòu)如下圖:

Paste_Image.png

至于徹底深拷貝咒程,幾乎是不可能實現(xiàn)的鸠天。有些專家級的程序員干脆從來不去覆蓋clone方法,也從來不去調(diào)用它帐姻,除非拷貝數(shù)組稠集。

public class ArrCopyTest {
        public static void main(String[] args) {
            String[] arr1=new String[]{"a","b","c"};
            ArrCopyTest.printArr(arr1);
            
            System.out.println("arr1copy8=arr1.clone()");
            String[] arr1copy8=arr1.clone();
            ArrCopyTest.printArr(arr1copy8);
            
            arr1[1]="well";//現(xiàn)在,我把原來數(shù)組arr1的值改變一下
            ArrCopyTest.printArr(arr1);
            ArrCopyTest.printArr(arr1copy8);
            
        }
        
        public static void printArr(String arr[]){
            if(arr==null){
                System.out.println("arr is null");
            }else{
                System.out.println("arr.hashCode is:"+arr.hashCode());
                for(int i=0;i<arr.length;i++){
                    System.out.println("arr["+i+"]:"+arr[i]);
                }
            }
        }
}
Paste_Image.png

clone顧名思義就是復制饥瓷, 在Java語言中剥纷, clone方法被對象調(diào)用,所以會復制對象。所謂的復制對象,首先要分配一個和源對象同樣大小的空間熟丸,在這個空間中創(chuàng)建一個新的對象。java創(chuàng)建對象的方式:
1 使用new操作符創(chuàng)建一個對象
2 使用clone方法復制一個對象
那么這兩種方式有什么相同和不同呢鳖宾? new操作符的本意是分配內(nèi)存吼砂。程序執(zhí)行到new操作符時逆航, 首先去看new操作符后面的類型,因為知道了類型渔肩,才能知道要分配多大的內(nèi)存空間因俐。分配完內(nèi)存之后,再調(diào)用構(gòu)造函數(shù)周偎,填充對象的各個域抹剩,這一步叫做對象的初始化,構(gòu)造方法返回后蓉坎,一個對象創(chuàng)建完畢澳眷,可以把他的引用(地址)發(fā)布到外部,在外部就可以使用這個引用操縱這個對象蛉艾。而clone在第一步是和new相似的钳踊, 都是分配內(nèi)存衷敌,調(diào)用clone方法時,分配的內(nèi)存和源對象(即調(diào)用clone方法的對象)相同拓瞪,然后再使用原對象中對應的各個域缴罗,填充新對象的域, 填充完成之后祭埂,clone方法返回面氓,一個新的相同的對象被創(chuàng)建,同樣可以把這個新對象的引用發(fā)布到外部蛆橡。

復制對象 or 復制引用

public class fz {
    public static void main(String[] args) {
        Person p = new Person(23, "zhang");  
        Person p1 = p;  
          
        System.out.println(p);  
        System.out.println(p1);  
    }
}
復制引用.png

當Person p1 = p;執(zhí)行之后舌界,通過打印結(jié)果可以看出,打印的地址值是相同的泰演,既然地址值都是相同的禀横,那么肯定是同一個對象。p和p1只是引用而已粥血,他們都指向了一個相同的對象Person(23柏锄,“zhang”)「纯鳎可以把這種現(xiàn)場叫做引用的復制趾娃。上面代碼執(zhí)行完成之后, 內(nèi)存中的情景如下圖所示:

Paste_Image.png

而下面的代碼是真真正正的克隆了一個對象缔御。

public class fz1 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person p = new Person(23, "zhang");  
        Person p1 = (Person) p.clone();  
          
        System.out.println(p);  
        System.out.println(p1);  
    }
}
Paste_Image.png

從打印結(jié)果可以看出抬闷,兩個對象的地址是不同的,也就是說創(chuàng)建了新的對象耕突, 而不是把原對象的地址賦給了一個新的引用變量笤成。以上代碼執(zhí)行完成后, 內(nèi)存中的情景如下圖所示:

Paste_Image.png

深拷貝 or 淺拷貝

深拷貝:復制對象
淺拷貝:復制引用(如果只是用Object中默認的clone方法眷茁,是淺拷貝)
上面的示例代碼中炕泳,Person中有兩個成員變量,分別是name和age上祈, name是String類型培遵, age是int類型。代碼非常簡單登刺,如下所示:

public class Person implements Cloneable{
    private int age ;  
    private String name;  
      
    public Person(int age, String name) {  
        this.age = age;  
        this.name = name;  
    }  
      
    public Person() {}  
  
    public int getAge() {  
        return age;  
    }  
  
    public String getName() {  
        return name;  
    }  
      
    @Override  
    protected Object clone() throws CloneNotSupportedException {  
        return (Person)super.clone();  
    } 
}

由于age是基本數(shù)據(jù)類型籽腕, 那么對它的拷貝沒有什么疑議,直接將一個4字節(jié)的整數(shù)值拷貝過來就行纸俭。但是name是String類型的皇耗, 它只是一個引用, 指向一個真正的String對象揍很,那么對它的拷貝有兩種方式: 直接將源對象中的name的引用值拷貝給新對象的name字段郎楼, 或者是根據(jù)原Person對象中的name指向的字符串對象創(chuàng)建一個新的相同的字符串對象矾瘾,將這個新字符串對象的引用賦給新拷貝的Person對象的name字段。這兩種拷貝方式分別叫做淺拷貝和深拷貝箭启。

下面通過代碼進行驗證壕翩。如果兩個Person對象的name的地址值相同, 說明兩個對象的name都指向同一個String對象傅寡, 也就是淺拷貝放妈, 而如果兩個對象的name的地址值不同, 那么就說明指向不同的String對象荐操, 也就是在拷貝Person對象的時候芜抒, 同時拷貝了name引用的String對象, 也就是深拷貝托启。驗證代碼如下:

public class fz2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person p = new Person(23, "zhang");  
        Person p1 = (Person) p.clone();  
          
        System.out.println("p.getName().hashCode() : " + p.getName().hashCode());  
        System.out.println("p1.getName().hashCode() : " + p1.getName().hashCode());  
          
        String result = p.getName().hashCode() == p1.getName().hashCode()   
                ? "clone是淺拷貝的" : "clone是深拷貝的";  
        System.out.println(result);  
    }
}
Paste_Image.png

覆蓋Object中的clone方法宅倒, 實現(xiàn)深拷貝

現(xiàn)在為了要在clone對象時進行深拷貝, 那么就要Clonable接口屯耸,覆蓋并實現(xiàn)clone方法拐迁,除了調(diào)用父類中的clone方法得到新的對象, 還要將該類中的引用變量也clone出來疗绣。如果只是用Object中默認的clone方法线召,是淺拷貝的,再次以下面的代碼驗證:

public class fz4 {
    static class Body implements Cloneable {
        public Head head;
        public Body() {
        }
        public Body(Head head) {
            this.head = head;
        }
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    static class Head /* implements Cloneable */{
        public Face face;
        public Head() {
        }
        public Head(Face face) {
            this.face = face;
        }
    }
    
    static class Face{} 
    public static void main(String[] args) throws CloneNotSupportedException {
        Body body = new Body(new Head());
        Body body1 = (Body) body.clone();
        System.out.println("body == body1 : " + (body == body1));
        System.out.println("body.head == body1.head : " + (body.head == body1.head));
    }
}
Paste_Image.png

在以上代碼中多矮, 有兩個主要的類缓淹, 分別為Body和head, 在Body類中塔逃, 組合了一個head對象讯壶。當對Body對象進行clone時, 它組合的head對象只進行淺拷貝湾盗。

如果要使Body對象在clone時進行深拷貝伏蚊, 那么就要在Body的clone方法中,將源對象引用的Head對象也clone一份淹仑。

public class fz4 {
    static class Body implements Cloneable {
        public Head head;
        public Body() {
        }
        public Body(Head head) {
            this.head = head;
        }
        @Override
        protected Object clone() throws CloneNotSupportedException {
//            return super.clone();
            Body newBody =  (Body) super.clone();  
            newBody.head = (Head) head.clone();  
            return newBody;
        }
    }
    static class Head  implements Cloneable {
        public Face face;
        public Head() {
        }
        public Head(Face face) {
            this.face = face;
        }
        
        @Override  
        protected Object clone() throws CloneNotSupportedException {  
                return super.clone();  
        }  
    }
    
    static class Face{} 
    public static void main(String[] args) throws CloneNotSupportedException {
        Body body = new Body(new Head());
        Body body1 = (Body) body.clone();
        System.out.println("body == body1 : " + (body == body1));
        System.out.println("body.head == body1.head : " + (body.head == body1.head));
    }
}
Paste_Image.png

由此可見丙挽, body和body1內(nèi)的head引用指向了不同的Head對象, 也就是說在clone Body對象的同時匀借, 也拷貝了它所引用的Head對象, 進行了深拷貝平窘。

真的是深拷貝嗎吓肋?

如果想要深拷貝一個對象, 這個對象必須要實現(xiàn)Cloneable接口瑰艘,實現(xiàn)clone方法是鬼,并且在clone方法內(nèi)部肤舞,把該對象引用的其他對象也要clone一份 , 這就要求這個被引用的對象必須也要實現(xiàn)Cloneable接口并且實現(xiàn)clone方法均蜜。
那么李剖,按照上面的結(jié)論, Body類組合了Head類囤耳, 而Head類組合了Face類篙顺,要想深拷貝Body類,必須在Body類的clone方法中將Head類也要拷貝一份充择,但是在拷貝Head類時德玫,默認執(zhí)行的是淺拷貝,也就是說Head中組合的Face對象并不會被拷貝椎麦。驗證代碼如下:

public class fz6 {
    static class Body implements Cloneable {
        public Head head;
        public Body() {
        }
        public Body(Head head) {
            this.head = head;
        }
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Body newBody = (Body) super.clone();
            newBody.head = (Head) head.clone();
            return newBody;
        }
    }
    static class Head implements Cloneable {
        public Face face;
        public Head() {
        }
        public Head(Face face) {
            this.face = face;
        }
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    static class Face {}
    public static void main(String[] args) throws CloneNotSupportedException {
        Body body = new Body(new Head(new Face()));
        Body body1 = (Body) body.clone();
        System.out.println("body == body1 : " + (body == body1));
        System.out.println("body.head == body1.head : " + (body.head == body1.head));
        System.out.println("body.head.face == body1.head.face : " + (body.head.face == body1.head.face));
    }
}
Paste_Image.png

內(nèi)存結(jié)構(gòu)圖如下圖所示:

Paste_Image.png

對于Body對象來說宰僧,其實應該算是深拷貝,因為對Body對象內(nèi)所引用的其他對象(目前只有Head)都進行了拷貝观挎,也就是說兩個獨立的Body對象內(nèi)的head引用已經(jīng)指向了獨立的兩個Head對象琴儿。但是,這對于兩個Head對象來說嘁捷,他們指向了同一個Face對象凤类,這就說明,兩個Body對象還是有一定的聯(lián)系普气,并沒有完全的獨立谜疤。這應該說是一種不徹底的深拷貝。

如何進行徹底的深拷貝

于上面的例子來說现诀,怎樣才能保證兩個Body對象完全獨立呢夷磕?只要在拷貝Head對象的時候,也將Face對象拷貝一份就可以了仔沿。這需要讓Face類也實現(xiàn)Cloneable接口坐桩,實現(xiàn)clone方法,并且在在Head對象的clone方法中封锉,拷貝它所引用的Face對象绵跷。修改的部分代碼如下:

   static class Head implements Cloneable {
        public Face face;
        public Head() {
        }
        public Head(Face face) {
            this.face = face;
        }
        @Override
        protected Object clone() throws CloneNotSupportedException {
            Head newHead =(Head)super.clone();
            newHead.face = (Face)face.clone();
            return newHead;
        }
    }
    static class Face implements Cloneable{
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
Paste_Image.png

這說名兩個Body已經(jīng)完全獨立了,他們間接引用的face對象已經(jīng)被拷貝成福,也就是引用了獨立的Face對象碾局。
依此類推,如果Face對象還引用了其他的對象奴艾, 比如說Mouth净当,如果不經(jīng)過處理,Body對象拷貝之后還是會通過一級一級的引用,引用到同一個Mouth對象像啼。同理俘闯, 如果要讓Body在引用鏈上完全獨立, 只能顯式的讓Mouth對象也被拷貝忽冻。到此真朗,可以得到如下結(jié)論:如果在拷貝一個對象時,要想讓這個拷貝的對象和源對象完全彼此獨立僧诚,那么在引用鏈上的每一級對象都要被顯式的拷貝遮婶。所以創(chuàng)建徹底的深拷貝是非常麻煩的,尤其是在引用關(guān)系非常復雜的情況下振诬, 或者在引用鏈的某一級上引用了一個第三方的對象蹭睡, 而這個對象沒有實現(xiàn)clone方法, 那么在它之后的所有引用的對象都是被共享的赶么。 舉例來說肩豁,如果被Head引用的Face類是第三方庫中的類,并且沒有實現(xiàn)Cloneable接口辫呻,那么在Face之后的所有對象都會被拷貝前后的兩個Body對象共同引用清钥。假設Face對象內(nèi)部組合了Mouth對象,并且Mouth對象內(nèi)部組合了Tooth對象放闺, 內(nèi)存結(jié)構(gòu)如下圖:

Paste_Image.png

至于徹底深拷貝祟昭,幾乎是不可能實現(xiàn)的。有些專家級的程序員干脆從來不去覆蓋clone方法怖侦,也從來不去調(diào)用它篡悟,除非拷貝數(shù)組。

public class ArrCopyTest {
        public static void main(String[] args) {
            String[] arr1=new String[]{"a","b","c"};
            ArrCopyTest.printArr(arr1);
            
            System.out.println("arr1copy8=arr1.clone()");
            String[] arr1copy8=arr1.clone();
            ArrCopyTest.printArr(arr1copy8);
            
            arr1[1]="well";//現(xiàn)在匾寝,我把原來數(shù)組arr1的值改變一下
            ArrCopyTest.printArr(arr1);
            ArrCopyTest.printArr(arr1copy8);
            
        }
        
        public static void printArr(String arr[]){
            if(arr==null){
                System.out.println("arr is null");
            }else{
                System.out.println("arr.hashCode is:"+arr.hashCode());
                for(int i=0;i<arr.length;i++){
                    System.out.println("arr["+i+"]:"+arr[i]);
                }
            }
        }
}
Paste_Image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末搬葬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子艳悔,更是在濱河造成了極大的恐慌急凰,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猜年,死亡現(xiàn)場離奇詭異抡锈,居然都是意外死亡,警方通過查閱死者的電腦和手機乔外,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門床三,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人袁稽,你說我怎么就攤上這事勿璃。” “怎么了推汽?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵补疑,是天一觀的道長。 經(jīng)常有香客問我歹撒,道長莲组,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任暖夭,我火速辦了婚禮锹杈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘迈着。我一直安慰自己竭望,他們只是感情好,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布裕菠。 她就那樣靜靜地躺著咬清,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奴潘。 梳的紋絲不亂的頭發(fā)上旧烧,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天,我揣著相機與錄音画髓,去河邊找鬼掘剪。 笑死,一個胖子當著我的面吹牛奈虾,可吹牛的內(nèi)容都是我干的夺谁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼肉微,長吁一口氣:“原來是場噩夢啊……” “哼匾鸥!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起浪册,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤扫腺,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后村象,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體笆环,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年厚者,在試婚紗的時候發(fā)現(xiàn)自己被綠了躁劣。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡库菲,死狀恐怖账忘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤鳖擒,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布溉浙,位于F島的核電站,受9級特大地震影響蒋荚,放射性物質(zhì)發(fā)生泄漏戳稽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一期升、第九天 我趴在偏房一處隱蔽的房頂上張望惊奇。 院中可真熱鬧,春花似錦播赁、人聲如沸颂郎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乓序。三九已至,卻和暖如春舟奠,著一層夾襖步出監(jiān)牢的瞬間竭缝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工沼瘫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留抬纸,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓耿戚,卻偏偏與公主長得像湿故,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子膜蛔,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法坛猪,類相關(guān)的語法,內(nèi)部類的語法皂股,繼承相關(guān)的語法墅茉,異常的語法,線程的語...
    子非魚_t_閱讀 31,622評論 18 399
  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,139評論 30 470
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理呜呐,服務發(fā)現(xiàn)就斤,斷路器,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • 月 闖入黑夜 使天空 淡去了幾分寂 烏云 不甘落寞 遮住了亮的月 黑夜 在安靜后 做了一場夢 夢中 只有一陣墨雨 ...
    醉酒的靈魂閱讀 83評論 0 1
  • 不甘興奮的短暫性 一瞬間涌動的想法 不顧后果的投入 用歇斯底里的狂歡來掩飾內(nèi)心的孤獨蘑辑。 不曾想 最大的充實就是與己...
    字爵閱讀 405評論 2 1