新聞中心
[[200191]]

創(chuàng)新互聯(lián)公司專注為客戶提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于成都網(wǎng)站制作、網(wǎng)站設(shè)計(jì)、遂平網(wǎng)絡(luò)推廣、微信小程序開(kāi)發(fā)、遂平網(wǎng)絡(luò)營(yíng)銷、遂平企業(yè)策劃、遂平品牌公關(guān)、搜索引擎seo、人物專訪、企業(yè)宣傳片、企業(yè)代運(yùn)營(yíng)等,從售前售中售后,我們都將竭誠(chéng)為您服務(wù),您的肯定,是我們最大的嘉獎(jiǎng);創(chuàng)新互聯(lián)公司為所有大學(xué)生創(chuàng)業(yè)者提供遂平建站搭建服務(wù),24小時(shí)服務(wù)熱線:18982081108,官方網(wǎng)址:www.cdcxhl.com
一、前言
任何變成語(yǔ)言中,其實(shí)都有淺拷貝和深拷貝的概念,Java 中也不例外。在對(duì)一個(gè)現(xiàn)有的對(duì)象進(jìn)行拷貝操作的時(shí)候,是有淺拷貝和深拷貝之分的,他們?cè)趯?shí)際使用中,區(qū)別很大,如果對(duì)其進(jìn)行混淆,可能會(huì)引發(fā)一些難以排查的問(wèn)題。
本文就在 Java 中的深拷貝和淺拷貝做一個(gè)詳細(xì)的解說(shuō)。
二、什么是淺拷貝和深拷貝
首先需要明白,淺拷貝和深拷貝都是針對(duì)一個(gè)已有對(duì)象的操作。那先來(lái)看看淺拷貝和深拷貝的概念。
在 Java 中,除了基本數(shù)據(jù)類型(元類型)之外,還存在 類的實(shí)例對(duì)象 這個(gè)引用數(shù)據(jù)類型。而一般使用 『 = 』號(hào)做賦值操作的時(shí)候。對(duì)于基本數(shù)據(jù)類型,實(shí)際上是拷貝的它的值,但是對(duì)于對(duì)象而言,其實(shí)賦值的只是這個(gè)對(duì)象的引用,將原對(duì)象的引用傳遞過(guò)去,他們實(shí)際上還是指向的同一個(gè)對(duì)象。
而淺拷貝和深拷貝就是在這個(gè)基礎(chǔ)之上做的區(qū)分,如果在拷貝這個(gè)對(duì)象的時(shí)候,只對(duì)基本數(shù)據(jù)類型進(jìn)行了拷貝,而對(duì)引用數(shù)據(jù)類型只是進(jìn)行了引用的傳遞,而沒(méi)有真實(shí)的創(chuàng)建一個(gè)新的對(duì)象,則認(rèn)為是淺拷貝。反之,在對(duì)引用數(shù)據(jù)類型進(jìn)行拷貝的時(shí)候,創(chuàng)建了一個(gè)新的對(duì)象,并且復(fù)制其內(nèi)的成員變量,則認(rèn)為是深拷貝。
所以到現(xiàn)在,就應(yīng)該了解了,所謂的淺拷貝和深拷貝,只是在拷貝對(duì)象的時(shí)候,對(duì) 類的實(shí)例對(duì)象 這種引用數(shù)據(jù)類型的不同操作而已。
總結(jié)來(lái)說(shuō):
1、淺拷貝:對(duì)基本數(shù)據(jù)類型進(jìn)行值傳遞,對(duì)引用數(shù)據(jù)類型進(jìn)行引用傳遞般的拷貝,此為淺拷貝。
2、深拷貝:對(duì)基本數(shù)據(jù)類型進(jìn)行值傳遞,對(duì)引用數(shù)據(jù)類型,創(chuàng)建一個(gè)新的對(duì)象,并復(fù)制其內(nèi)容,此為深拷貝。
三、Java 中的 clone()
3.1 Object 上的 clone() 方法
在 Java 中,所有的 Class 都繼承自 Object ,而在 Object 上,存在一個(gè) clone() 方法,它被聲明為了 protected ,所以我們可以在其子類中,使用它。
而無(wú)論是淺拷貝還是深拷貝,都需要實(shí)現(xiàn) clone() 方法,來(lái)完成操作。
可以看到,它的實(shí)現(xiàn)非常的簡(jiǎn)單,它限制所有調(diào)用 clone() 方法的對(duì)象,都必須實(shí)現(xiàn) Cloneable 接口,否者將拋出 CloneNotSupportedException 這個(gè)異常。最終會(huì)調(diào)用 internalClone() 方法來(lái)完成具體的操作。而 internalClone() 方法,實(shí)則是一個(gè)native 的方法。對(duì)此我們就沒(méi)必要深究了,只需要知道它可以 clone() 一個(gè)對(duì)象得到一個(gè)新的對(duì)象實(shí)例即可。
而反觀 Cloneable 接口,可以看到它其實(shí)什么方法都不需要實(shí)現(xiàn)。對(duì)他可以簡(jiǎn)單的理解只是一個(gè)標(biāo)記,是開(kāi)發(fā)者允許這個(gè)對(duì)象被拷貝。
3.2 淺拷貝
先來(lái)看看淺拷貝的例子。
首先創(chuàng)建一個(gè) class 為 FatherClass ,對(duì)其實(shí)現(xiàn) Cloneable 接口,并且重寫 clone()方法。
然后先正常 new 一個(gè) FatherClass 對(duì)象,再使用 clone() 方法創(chuàng)建一個(gè)新的對(duì)象。
***看看輸出的 Log :
- I/cxmyDev: fatherA == fatherB : false
- I/cxmyDev: fatherA hash : 560973324
- I/cxmyDev: fatherB hash : 560938740
- I/cxmyDev: fatherA name : 張三
- I/cxmyDev: fatherB name : 張三
可以看到,使用 clone() 方法,從 == 和 hashCode 的不同可以看出,clone() 方法實(shí)則是真的創(chuàng)建了一個(gè)新的對(duì)象。
但這只是一次淺拷貝的操作。
來(lái)驗(yàn)證這一點(diǎn),繼續(xù)看下去,在 FatherClass 中,還有一個(gè) ChildClass 的對(duì)象 child ,clone() 方法是否也可以正常復(fù)制它呢?改寫一個(gè)上面的 Demo。
看到,這里將其內(nèi)的 child 進(jìn)行負(fù)責(zé),用起來(lái)看看輸出的 Log 效果。
- I/cxmyDev: fatherA == fatherB : false
- I/cxmyDev: fatherA hash : 560975188
- I/cxmyDev: fatherB hash : 560872384
- I/cxmyDev: fatherA name : 張三
- I/cxmyDev: fatherB name : 張三
- I/cxmyDev: ==================
- I/cxmyDev: A.child == B.child : true
- I/cxmyDev: fatherA.child hash : 560891436
- I/cxmyDev: fatherB.child hash : 560891436
從***對(duì) child 的輸出可以看到,A 和 B 的 child 對(duì)象,實(shí)際上還是指向了同一個(gè)對(duì)象,只對(duì)對(duì)它的引用進(jìn)行了傳遞。
3.3 深拷貝
既然已經(jīng)了解了對(duì) clone() 方法,只能對(duì)當(dāng)前對(duì)象進(jìn)行淺拷貝,引用類型依然是在傳遞引用。
那么,如何進(jìn)行一個(gè)深拷貝呢?
比較常用的方案有兩種:
- 序列化(serialization)這個(gè)對(duì)象,再反序列化回來(lái),就可以得到這個(gè)新的對(duì)象,無(wú)非就是序列化的規(guī)則需要我們自己來(lái)寫。
- 繼續(xù)利用 clone() 方法,既然 clone() 方法,是我們來(lái)重寫的,實(shí)際上我們可以對(duì)其內(nèi)的引用類型的變量,再進(jìn)行一次 clone()。
繼續(xù)改寫上面的 Demo ,讓 ChildClass 也實(shí)現(xiàn) Cloneable 接口。
最重要的代碼就在 FatherClass.clone() 中,它對(duì)其內(nèi)的 child ,再進(jìn)行了一次 clone() 操作。
再來(lái)看看輸出的 Log。
- I/cxmyDev: fatherA == fatherB : false
- I/cxmyDev: fatherA hash : 561056732
- I/cxmyDev: fatherB hash : 561057344
- I/cxmyDev: fatherA name : 張三
- I/cxmyDev: fatherB name : 張三
- I/cxmyDev: ==================
- I/cxmyDev: A.child == B.child : false
- I/cxmyDev: fatherA.child hash : 561057304
- I/cxmyDev: fatherB.child hash : 561057360
可以看到,對(duì) child 也進(jìn)行了一次拷貝,這實(shí)則是對(duì) ChildClass 進(jìn)行的淺拷貝,但是對(duì)于 FatherClass 而言,則是一次深拷貝。
其實(shí)深拷貝的思路都差不多,序列化也好,使用 clone() 也好,實(shí)際上都是需要我們自己來(lái)編寫拷貝的規(guī)則,最終實(shí)現(xiàn)深拷貝的目的。
如果想要實(shí)現(xiàn)深拷貝,推薦使用 clone() 方法,這樣只需要每個(gè)類自己維護(hù)自己即可,而無(wú)需關(guān)心內(nèi)部其他的對(duì)象中,其他的參數(shù)是否也需要 clone() 。
四、總結(jié)
到現(xiàn)在基本上就已經(jīng)梳理清楚,Java 中淺拷貝和深拷貝的概念了。
實(shí)則淺拷貝和深拷貝只是相對(duì)的,如果一個(gè)對(duì)象內(nèi)部只有基本數(shù)據(jù)類型,那用 clone() 方法獲取到的就是這個(gè)對(duì)象的深拷貝,而如果其內(nèi)部還有引用數(shù)據(jù)類型,那用 clone() 方法就是一次淺拷貝的操作。
網(wǎng)頁(yè)名稱:細(xì)說(shuō)Java的深拷貝和淺拷貝
本文來(lái)源:http://fisionsoft.com.cn/article/djojssd.html


咨詢
建站咨詢
