版權聲明:spa
本帳號發佈文章均來自公衆號,承香墨影(cxmyDev),版權歸承香墨影全部。code
未經容許,不得轉載。對象
任何變成語言中,其實都有淺拷貝和深拷貝的概念,Java 中也不例外。在對一個現有的對象進行拷貝操做的時候,是有淺拷貝和深拷貝之分的,他們在實際使用中,區別很大,若是對其進行混淆,可能會引起一些難以排查的問題。繼承
本文就在 Java 中的深拷貝和淺拷貝作一個詳細的解說。接口
首先須要明白,淺拷貝和深拷貝都是針對一個已有對象的操做。那先來看看淺拷貝和深拷貝的概念。開發
在 Java 中,除了基本數據類型(元類型)以外,還存在 類的實例對象 這個引用數據類型。而通常使用 『 = 』號作賦值操做的時候。對於基本數據類型,其實是拷貝的它的值,可是對於對象而言,其實賦值的只是這個對象的引用,將原對象的引用傳遞過去,他們實際上仍是指向的同一個對象。rem
而淺拷貝和深拷貝就是在這個基礎之上作的區分,若是在拷貝這個對象的時候,只對基本數據類型進行了拷貝,而對引用數據類型只是進行了引用的傳遞,而沒有真實的建立一個新的對象,則認爲是淺拷貝。反之,在對引用數據類型進行拷貝的時候,建立了一個新的對象,而且複製其內的成員變量,則認爲是深拷貝。hash
因此到如今,就應該瞭解了,所謂的淺拷貝和深拷貝,只是在拷貝對象的時候,對 類的實例對象 這種引用數據類型的不一樣操做而已。it
總結來講:io
一、淺拷貝:對基本數據類型進行值傳遞,對引用數據類型進行引用傳遞般的拷貝,此爲淺拷貝。
二、深拷貝:對基本數據類型進行值傳遞,對引用數據類型,建立一個新的對象,並複製其內容,此爲深拷貝。
在 Java 中,全部的 Class 都繼承自 Object ,而在 Object 上,存在一個 clone() 方法,它被聲明爲了 protected
,因此咱們能夠在其子類中,使用它。
而不管是淺拷貝仍是深拷貝,都須要實現 clone() 方法,來完成操做。
能夠看到,它的實現很是的簡單,它限制全部調用 clone() 方法的對象,都必須實現 Cloneable
接口,否者將拋出 CloneNotSupportedException
這個異常。最終會調用 internalClone()
方法來完成具體的操做。而 internalClone()
方法,實則是一個 native 的方法。對此咱們就不必深究了,只須要知道它能夠 clone()
一個對象獲得一個新的對象實例便可。
而反觀 Cloneable 接口,能夠看到它其實什麼方法都不須要實現。對他能夠簡單的理解只是一個標記,是開發者容許這個對象被拷貝。
先來看看淺拷貝的例子。
首先建立一個 class 爲 FatherClass ,對其實現 Cloneable 接口,而且重寫 clone()
方法。
而後先正常 new 一個 FatherClass 對象,再使用 clone() 方法建立一個新的對象。
最後看看輸出的 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()
方法實則是真的建立了一個新的對象。
但這只是一次淺拷貝的操做。
來驗證這一點,繼續看下去,在 FatherClass 中,還有一個 ChildClass 的對象 child ,clone() 方法是否也能夠正常複製它呢?改寫一個上面的 Demo。
看到,這裏將其內的 child 進行負責,用起來看看輸出的 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
從最後對 child 的輸出能夠看到,A 和 B 的 child 對象,實際上仍是指向了統一個對象,只對對它的引用進行了傳遞。
既然已經瞭解了對 clone() 方法,只能對當前對象進行淺拷貝,引用類型依然是在傳遞引用。
那麼,如何進行一個深拷貝呢?
比較經常使用的方案有兩種:
繼續改寫上面的 Demo ,讓 ChildClass 也實現 Cloneable 接口。
最重要的代碼就在 FatherClass.clone() 中,它對其內的 child ,再進行了一次 clone() 操做。
再來看看輸出的 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
能夠看到,對 child 也進行了一次拷貝,這實則是對 ChildClass 進行的淺拷貝,可是對於 FatherClass 而言,則是一次深拷貝。
其實深拷貝的思路都差很少,序列化也好,使用 clone() 也好,實際上都是須要咱們本身來編寫拷貝的規則,最終實現深拷貝的目的。
若是想要實現深拷貝,推薦使用 clone() 方法,這樣只須要每一個類本身維護本身便可,而無需關心內部其餘的對象中,其餘的參數是否也須要 clone() 。
到如今基本上就已經梳理清楚,Java 中淺拷貝和深拷貝的概念了。
實則淺拷貝和深拷貝只是相對的,若是一個對象內部只有基本數據類型,那用 clone() 方法獲取到的就是這個對象的深拷貝,而若是其內部還有引用數據類型,那用 clone() 方法就是一次淺拷貝的操做。