1. 深入理解淺拷貝和深拷貝

        共 2807字,需瀏覽 6分鐘

         ·

        2022-04-09 22:12

        點(diǎn)擊上方“程序員大白”,選擇“星標(biāo)”公眾號(hào)

        重磅干貨,第一時(shí)間送達(dá)


        0x01:概述

        Java中的對(duì)象拷貝 ( Object Copy ) 是指將一個(gè)對(duì)象的所有屬性(成員變量)拷貝到另一個(gè)有著相同類類型的對(duì)象中去。例如,對(duì)象 A 和對(duì)象 B 都屬于類 S,具有屬性 a 和 b。那么對(duì)對(duì)象 A 進(jìn)行拷貝操作賦值給對(duì)象 B 就是:

        B.a?=?A.a;??
        B.b?=?A.b;

        拷貝對(duì)象是很常見的,主要是為了在新的上下文環(huán)境中復(fù)用現(xiàn)有對(duì)象的部分或全部數(shù)據(jù)。Java中的對(duì)象拷貝主要分為

        • 淺拷貝( Shallow Copy )

        • 深拷貝( Deep Copy )

        Java中的數(shù)據(jù)類型分為基本數(shù)據(jù)類型引用數(shù)據(jù)類型。對(duì)于這兩種數(shù)據(jù)類型,在進(jìn)行賦值操作、用作方法參數(shù)或返回值時(shí),會(huì)有值傳遞和引用(地址)傳遞的差別。

        淺拷貝(Shallow Copy)

        • 對(duì)于數(shù)據(jù)類型是基本數(shù)據(jù)類型的成員變量,淺拷貝會(huì)直接進(jìn)行值傳遞,也就是將該屬性值復(fù)制一份給新的對(duì)象。因?yàn)槭莾煞莶煌臄?shù)據(jù),所以對(duì)其中一個(gè)對(duì)象的該成員變量值進(jìn)行修改,不會(huì)影響另一個(gè)對(duì)象拷貝得到的數(shù)據(jù)。

        • 對(duì)于數(shù)據(jù)類型是引用數(shù)據(jù)類型的成員變量,比如說成員變量是某個(gè)數(shù)組、某個(gè)類的對(duì)象等,那么淺拷貝會(huì)進(jìn)行引用傳遞,也就是只是將該成員變量的引用值(內(nèi)存地址)復(fù)制一份給新的對(duì)象。因?yàn)閷?shí)際上兩個(gè)對(duì)象的該成員變量都指向同一個(gè)實(shí)例。在這種情況下,在一個(gè)對(duì)象中修改該成員變量會(huì)影響到另一個(gè)對(duì)象的該成員變量值。

        具體模型如下圖所示,可以看到基本數(shù)據(jù)類型的成員變量,對(duì)其值創(chuàng)建了新的拷貝;而引用數(shù)據(jù)類型的成員變量的實(shí)例仍然是只有一份,兩個(gè)對(duì)象的該成員變量都指向同一個(gè)實(shí)例。

        0x02:淺拷貝的實(shí)現(xiàn)方式

        • 拷貝構(gòu)造方法實(shí)現(xiàn)淺拷貝

        拷貝構(gòu)造方法指的是該類的構(gòu)造方法參數(shù)為該類的對(duì)象。使用拷貝構(gòu)造方法可以很好地完成淺拷貝,直接通過一個(gè)現(xiàn)有的對(duì)象創(chuàng)建出與該對(duì)象屬性相同的新的對(duì)象。

        • 重寫clone()方法進(jìn)行淺拷貝

        Object類是類結(jié)構(gòu)的根類,其中有一個(gè)方法

        protected?Object?clone()?throws?CloneNotSupportedException

        這個(gè)方法就是進(jìn)行的淺拷貝。有了這個(gè)淺拷貝模板,可以通過調(diào)用clone()方法來實(shí)現(xiàn)對(duì)象的淺拷貝。但是需要注意:

        (1)Object類雖然有這個(gè)方法,但是這個(gè)方法是受保護(hù)的(被protected修飾),所以無法直接使用。

        (2)使用clone方法的類必須實(shí)現(xiàn)Cloneable接口,否則會(huì)拋出異常CloneNotSupportedException。

        對(duì)于這兩點(diǎn),我們的解決方法是:在要使用clone方法的類中重寫clone()方法,通過super.clone()調(diào)用Object類中的原clone方法。


        0x03:深拷貝的實(shí)現(xiàn)方式

        首先介紹對(duì)象圖的概念。設(shè)想一下,一個(gè)類有一個(gè)對(duì)象,其成員變量中又有一個(gè)對(duì)象,該對(duì)象指向另一個(gè)對(duì)象,另一個(gè)對(duì)象又指向另一個(gè)對(duì)象,直到一個(gè)確定的實(shí)例。這就形成了對(duì)象圖。那么對(duì)于深拷貝來說,不僅要復(fù)制對(duì)象的所有基本數(shù)據(jù)類型的成員變量值,還要為所有引用數(shù)據(jù)類型的成員變量申請(qǐng)存儲(chǔ)空間,并復(fù)制每個(gè)引用數(shù)據(jù)類型成員變量所引用的對(duì)象,直到該對(duì)象可達(dá)的所有對(duì)象。也就是說,對(duì)象進(jìn)行深拷貝要對(duì)整個(gè)對(duì)象圖進(jìn)行拷貝。

        一句話,深拷貝對(duì)引用數(shù)據(jù)類型的成員變量的對(duì)象圖中所有的對(duì)象都開辟了內(nèi)存空間;而淺拷貝只是傳遞地址指向,新的對(duì)象并沒有對(duì)引用數(shù)據(jù)類型創(chuàng)建內(nèi)存空間。深拷貝模型如下圖所示,可以看到所有的成員變量都進(jìn)行了復(fù)制。

        因?yàn)閯?chuàng)建內(nèi)存空間和拷貝整個(gè)對(duì)象圖,所以深拷貝相比于淺拷貝速度較慢并且花銷較大。

        • 重寫clone方法來實(shí)現(xiàn)深拷貝

        與通過重寫clone方法實(shí)現(xiàn)淺拷貝的基本思路一樣,只需要為對(duì)象圖的每一層的每一個(gè)對(duì)象都實(shí)現(xiàn)Cloneable接口并重寫clone方法,最后在最頂層的類的重寫的clone方法中調(diào)用所有的clone方法即可實(shí)現(xiàn)深拷貝。簡單的說只要每一層的每個(gè)對(duì)象都進(jìn)行淺拷貝,就等于實(shí)現(xiàn)了深拷貝。

        @Override
        public?Object?clone()?{
        ????//深拷貝
        ????try?{
        ????????//?直接調(diào)用父類的clone()方法
        ????????Student?student?=?(Student)?super.clone();
        ????????student.引用對(duì)象?=?(引用對(duì)象)?引用對(duì)象.clone();
        ????????return?student;
        ????}?catch?(CloneNotSupportedException?e)?{
        ????????return?null;
        ????}
        }
        • 對(duì)象序列化實(shí)現(xiàn)深拷貝

        雖然層次調(diào)用clone方法可以實(shí)現(xiàn)深拷貝,但是顯然代碼量實(shí)在太大。特別對(duì)于屬性數(shù)量比較多、層次比較深的類而言,每個(gè)類都要重寫clone方法太過繁瑣。將對(duì)象序列化為字節(jié)序列后,默認(rèn)會(huì)將該對(duì)象的整個(gè)對(duì)象圖進(jìn)行序列化,再通過反序列即可完美地實(shí)現(xiàn)深拷貝。

        //將對(duì)象寫入流中
        ByteArrayOutputStream?outputStream?=?new?ByteArrayOutputStream();
        ObjectOutputStream?objectOutputStream?=?new?ObjectOutputStream(outputStream);
        objectOutputStream.writeObject(拷貝對(duì)象);

        //從流中取出
        ByteArrayInputStream?inputStream?=?new?ByteArrayInputStream(outputStream.toByteArray());
        ObjectInputStream?objectInputStream?=?new?ObjectInputStream(inputStream);
        return?(拷貝對(duì)象)objectInputStream.readObject();
        • JSON或者XML方式實(shí)現(xiàn)深拷貝

        因?yàn)橐粋€(gè)POJO對(duì)象可以通過JSON庫變成一個(gè)json字符串(通過XML庫變成一個(gè)xml字符串),再通過對(duì)應(yīng)的類庫又反序列化成另外一個(gè)完整的對(duì)象。

        String?json?=JSON.toJSONString(src);
        T?object?=?JSON.parseObject(json,?clazz);

        國產(chǎn)小眾瀏覽器因屏蔽視頻廣告,被索賠100萬(后續(xù))

        年輕人“不講武德”:因看黃片上癮,把網(wǎng)站和786名女主播起訴了

        中國聯(lián)通官網(wǎng)被發(fā)現(xiàn)含木馬腳本,可向用戶推廣色情APP

        張一鳴:每個(gè)逆襲的年輕人,都具備的底層能力


        關(guān)


        ,學(xué),西學(xué)學(xué)運(yùn)護(hù)號(hào),質(zhì),結(jié)識(shí),關(guān)[],學(xué)習(xí)進(jìn)!



        瀏覽 43
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
          
          

            1. v天堂在线 | 少妇人妻AV| 插菊花综合网2 | 国产精品视频中文字幕 | 欧美黄色三级大片 |