深復(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