Java中的深復(fù)制和淺復(fù)制

深復(fù)制和淺復(fù)制也稱為深拷貝和淺拷貝核芽。簡(jiǎn)單的說(shuō)就是創(chuàng)建一個(gè)和當(dāng)前對(duì)象一模一樣的對(duì)象。在日常編碼的過(guò)程中使用的幾率并不多酵熙,但在面試中卻會(huì)被經(jīng)常問到轧简。

了解深復(fù)制和淺復(fù)制的原理后,可以幫助我們對(duì)Java中的值傳遞和引用傳遞有更深刻的理解匾二。

淺復(fù)制

下面的實(shí)例中哮独,我們創(chuàng)建一個(gè)原始類Monster,調(diào)用對(duì)象的clone方法復(fù)制一個(gè)新的對(duì)象察藐。

注意:要調(diào)用對(duì)象的clone方法皮璧,需要讓類實(shí)現(xiàn)Cloneable接口,并重寫clone方法

publicclassMonsterimplementsCloneable{privateString name;//怪物名稱privateintlevel;//怪物級(jí)別privatePoint position;//怪物坐標(biāo)publicMonster(String name,intlevel){this.name=name;this.level=level;position=newPoint(100,100);}@OverrideprotectedObjectclone()throwsCloneNotSupportedException{//Cloneable接口僅僅是一個(gè)標(biāo)志分飞,而且這個(gè)標(biāo)志也僅僅是針對(duì) Object類中 clone()方法的悴务,//如果 clone類沒有實(shí)現(xiàn) Cloneable接口钟鸵,并調(diào)用了 Object的 clone()方法//(即調(diào)用super.clone()方法)箱玷,那么Object的 clone()方法就會(huì)拋出 CloneNotSupportedException異常。returnsuper.clone();}publicStringgetName(){returnname;}publicvoidsetName(String name){this.name=name;}publicintgetLevel(){returnlevel;}publicvoidsetLevel(intlevel){this.level=level;}publicPointgetPosition(){returnposition;}publicvoidsetPosition(intx,inty){position.setX(x);position.setY(y);}}classPoint{//坐標(biāo)類privateintx;privateinty;publicPoint(intx,inty){this.x=x;this.y=y;}publicintgetX(){returnx;}publicvoidsetX(intx){this.x=x;}publicintgetY(){returny;}publicvoidsetY(inty){this.y=y;}}

publicclassMonsterTest{publicstaticvoidmain(String[]args)throwsCloneNotSupportedException{Monster monster1=newMonster("史萊克",99);//通過(guò)clone方法摆霉,將對(duì)象monster1中的值復(fù)制到對(duì)象monster2Monster monster2=(Monster)monster1.clone();//1.monster1和monster2是兩個(gè)對(duì)象,下面的結(jié)果是falseSystem.out.println(monster1.equals(monster2));//2.對(duì)比對(duì)象中的值(淺復(fù)制中染服,只復(fù)制了對(duì)象的引用)System.out.println(monster1.getName()==monster2.getName());System.out.println(monster1.getName().hashCode());System.out.println(monster2.getName().hashCode());//鞏固第二點(diǎn)别洪,修改monster2的坐標(biāo)值monster2.setPosition(111,111);//monster2的坐標(biāo)值修改后,對(duì)象monster1的坐標(biāo)值也被修改了System.out.println(monster1.getPosition().getX()+"\t"+monster1.getPosition().getY());}}

我們可以通過(guò)這個(gè)例子體會(huì)淺復(fù)制的特點(diǎn):

被復(fù)制對(duì)象的所有成員屬性都有與原來(lái)的對(duì)象相同的值柳刮,而所有的對(duì)其他對(duì)象的引用仍然指向原來(lái)的對(duì)象挖垛。

即淺復(fù)制后,新對(duì)象的地址變了秉颗,但新對(duì)象中字段(屬性)所指向的內(nèi)存地址(引用)卻沒變痢毒,新舊對(duì)象的字段指向了同一塊內(nèi)存空間。

深復(fù)制

深復(fù)制則是指在clone對(duì)象本身站宗,也clone對(duì)象中中的屬性(字段)

我們可以通過(guò)下面的代碼體會(huì):

publicclassMonsterimplementsCloneable{privateString name;privateintlevel;privatePoint position;publicMonster(String name,intlevel){this.name=name;this.level=level;position=newPoint(100,100);}@OverrideprotectedObjectclone()throwsCloneNotSupportedException{Monster monster=(Monster)super.clone();//對(duì)象屬性也通過(guò)clone方法復(fù)制-深復(fù)制monster.position=(Point)position.clone();returnmonster;}publicStringgetName(){returnname;}publicvoidsetName(String name){this.name=name;}publicintgetLevel(){returnlevel;}publicvoidsetLevel(intlevel){this.level=level;}publicPointgetPosition(){returnposition;}publicvoidsetPosition(intx,inty){position.setX(x);position.setY(y);}}classPointimplementsCloneable{privateintx;privateinty;publicPoint(intx,inty){this.x=x;this.y=y;}publicintgetX(){returnx;}publicvoidsetX(intx){this.x=x;}publicintgetY(){returny;}publicvoidsetY(inty){this.y=y;}@OverrideprotectedObjectclone()throwsCloneNotSupportedException{//作為屬性類同樣實(shí)現(xiàn)clone方法闸准,就實(shí)現(xiàn)了深復(fù)制returnsuper.clone();}}

publicclassMonsterTest{publicstaticvoidmain(String[]args)throwsCloneNotSupportedException{Monster monster1=newMonster("史萊克",99);//通過(guò)clone方法益愈,將對(duì)象monster1中的值復(fù)制到對(duì)象monster2Monster monster2=(Monster)monster1.clone();//1.monster1和monster2是兩個(gè)對(duì)象,下面的結(jié)果是false - 同淺復(fù)制System.out.println(monster1.equals(monster2));//2.對(duì)比對(duì)象中的值(淺復(fù)制中梢灭,只復(fù)制了對(duì)象的引用) - 淺復(fù)制System.out.println(monster1.getName()==monster2.getName());System.out.println(monster1.getName().hashCode());System.out.println(monster2.getName().hashCode());//修改monster2的坐標(biāo)值monster2.setPosition(111,111);//深復(fù)制后,monster2的坐標(biāo)值修改蒸其,對(duì)象monster1的坐標(biāo)值并沒有被修改System.out.println(monster1.getPosition().getX()+"\t"+monster1.getPosition().getY());}}

上面的代碼雖然實(shí)現(xiàn)了深復(fù)制敏释,但是有個(gè)瑕疵,就是如果Monster類中出現(xiàn)自定義的引用類型屬性(字段)摸袁,這個(gè)引用類型就需要實(shí)現(xiàn)Cloneable接口并重寫clone方法钥顽。引用類型多的情況下,會(huì)增加代碼量靠汁。

實(shí)現(xiàn)深復(fù)制的另一種方法就是使用序列化技術(shù):

序列化是將對(duì)象寫到流中便于傳輸蜂大,而反序列化則是把對(duì)象從流中讀取出來(lái)闽铐。這里寫到流中的對(duì)象則是原始對(duì)象的一個(gè)拷貝,因?yàn)樵紝?duì)象還存在 JVM 中奶浦,所以我們可以利用對(duì)象的序列化產(chǎn)生克隆對(duì)象兄墅,然后通過(guò)反序列化獲取這個(gè)對(duì)象。

  注意每個(gè)需要序列化的類都要實(shí)現(xiàn) Serializable 接口澳叉,如果有某個(gè)屬性不需要序列化隙咸,可以將其聲明為 transient,即將其排除在克隆屬性之外成洗。

importjava.io.ByteArrayInputStream;importjava.io.ByteArrayOutputStream;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;importjava.io.Serializable;publicclassMonsterimplementsSerializable{privatestaticfinallongserialVersionUID=1L;privateString name;privateintlevel;privatePoint position;publicMonster(String name,intlevel){this.name=name;this.level=level;position=newPoint(100,100);}//深復(fù)制publicObjectdeepClone()throwsException{// 序列化ByteArrayOutputStream bos=newByteArrayOutputStream();ObjectOutputStream oos=newObjectOutputStream(bos);oos.writeObject(this);// 反序列化ByteArrayInputStream bis=newByteArrayInputStream(bos.toByteArray());ObjectInputStream ois=newObjectInputStream(bis);returnois.readObject();}publicStringgetName(){returnname;}publicvoidsetName(String name){this.name=name;}publicintgetLevel(){returnlevel;}publicvoidsetLevel(intlevel){this.level=level;}publicPointgetPosition(){returnposition;}publicvoidsetPosition(intx,inty){position.setX(x);position.setY(y);}}classPointimplementsSerializable{privatestaticfinallongserialVersionUID=1L;privateintx;privateinty;publicPoint(intx,inty){this.x=x;this.y=y;}publicintgetX(){returnx;}publicvoidsetX(intx){this.x=x;}publicintgetY(){returny;}publicvoidsetY(inty){this.y=y;}}

publicclassMonsterTest{publicstaticvoidmain(String[]args)throwsException{Monster monster1=newMonster("史萊克",99);//調(diào)用自定義的deepClone方法(深復(fù)制)五督,將對(duì)象monster1中的值復(fù)制到對(duì)象monster2Monster monster2=(Monster)monster1.deepClone();//1.monster1和monster2是兩個(gè)對(duì)象,下面的結(jié)果是false System.out.println(monster1.equals(monster2));//2.對(duì)比對(duì)象中的值System.out.println(monster1.getName()==monster2.getName());System.out.println(monster1.getName().hashCode());System.out.println(monster2.getName().hashCode());//修改monster2的坐標(biāo)值monster2.setPosition(111,111);//深復(fù)制后,monster2的坐標(biāo)值修改瓶殃,對(duì)象monster1的坐標(biāo)值并沒有被修改System.out.println(monster1.getPosition().getX()+"\t"+monster1.getPosition().getY());}}


老九學(xué)堂出品充包,轉(zhuǎn)載請(qǐng)私信哦

對(duì)于文章內(nèi)容有不理解的可以添加老九君個(gè)人QQ:614940318,請(qǐng)備注來(lái)自簡(jiǎn)書

老九學(xué)堂免費(fèi)C遥椿、C++误证、Java課程地址:

https://study.163.com/courses-search?keyword=%E8%80%81%E4%B9%9D%E5%AD%A6%E5%A0%82

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市修壕,隨后出現(xiàn)的幾起案子愈捅,更是在濱河造成了極大的恐慌,老刑警劉巖慈鸠,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蓝谨,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡青团,警方通過(guò)查閱死者的電腦和手機(jī)譬巫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)督笆,“玉大人芦昔,你說(shuō)我怎么就攤上這事⊥拗祝” “怎么了咕缎?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)料扰。 經(jīng)常有香客問我凭豪,道長(zhǎng),這世上最難降的妖魔是什么晒杈? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任嫂伞,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘帖努。我一直安慰自己撰豺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布拼余。 她就那樣靜靜地躺著郑趁,像睡著了一般。 火紅的嫁衣襯著肌膚如雪姿搜。 梳的紋絲不亂的頭發(fā)上寡润,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音舅柜,去河邊找鬼梭纹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛致份,可吹牛的內(nèi)容都是我干的变抽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼氮块,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼绍载!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起滔蝉,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤击儡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蝠引,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阳谍,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年螃概,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了矫夯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡吊洼,死狀恐怖训貌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情冒窍,我是刑警寧澤递沪,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站超燃,受9級(jí)特大地震影響区拳,放射性物質(zhì)發(fā)生泄漏拘领。R本人自食惡果不足惜意乓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧届良,春花似錦笆凌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至慢显,卻和暖如春爪模,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背荚藻。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工屋灌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人应狱。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓共郭,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親疾呻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子除嘹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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