面試時的尴尬瞬間
聊到克隆,不禁想起了自己懵懂無知時的一個面試。
面試官:Java中建立對象的方式有哪些?
我:有構造方法、反射,其他的應該沒了吧。
然後面試官笑笑沒說話,面試差不多結束時。
我:Java中建立對象的方式還有哪些?
面試官:還有序列化和克隆。
我:...
面試官:...
......
回到家後就檢視了相關的部落格資料,先對克隆做進一步的了解。看過之後還是一知半解的狀态,就在最近學習的一個視訊中,老師對這部分進行講解後,我才有了一種豁然開朗感覺,希望在這裡分享給大家。
下面我就照着自己的了解,通過講述克隆中的深克隆與淺克隆,來讓大家能夠了解克隆這樣一個概念,希望大家以後在面試或工作中都能夠用到。
廢話不多說,由淺入深,直接來Coding、Debug
淺克隆
一、建立一個基礎類Person,擁有屬性name(基礎資料類型)和birthday(引用資料類型),并讓其通過實作Cloneable接口并重寫clone方法來實作克隆。
(杠精注意:這裡必須要實作Cloneable接口和重寫clone方法才能實作克隆,當然你說我通過繼承父類的,那我隻能說你能實作就好。)
public class Person implements Cloneable {
private String name;
private Date birthday;
public Person() {}
public Person(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
二、建立一個測試類進行測試,經過克隆後的對象還是不是同一個對象?
@Test
public void test1() throws CloneNotSupportedException {
Person person1 = new Person("lihui", new Date(0L));
Person person2 = (Person) person1.clone();
System.out.println(person1);
System.out.println(person2);
System.out.println(person1 == person2);
}
結果分析:
1、列印結果(1、2行)列印的(類名 + @ + hashcode轉16進制)是不同的。這個你想讓它看起來相同也很簡單,你就重寫hashCode方法就行了。(因為我在使用lombok的Data注解時,就因為它會自動重寫hashCode方法,我當時看到的他們就是相同的);
2、列印結果(3行)列印的兩者比較的值是不同的,說明已經克隆出了一個新對象。
三、Debug進一步檢視對象的實際記憶體配置設定,跑起來...

淺克隆記憶體分析圖
結果分析:
1、這裡假設數字就是記憶體位址:person1的記憶體位址是861,person2的記憶體位址是882,他們的記憶體位址是不同的,是以上述中的比較結果自然是:false;
2、仔細看圖你會發現person1和person2的引用類型屬性birthday的記憶體位址是相同的,奧,,,原來它僅僅克隆了這個對象最表層的東西,内部的引用類型屬性都沒改變,是以預設重寫的clone方法是一種淺克隆。
(有多淺,就像你去遊泳,遊泳池的水才到你的腳面。)
注意:在這種預設重寫的clone方法下,一個對象A被建立了,然後對象B是通過對象A克隆得到的,那麼僅僅是對象A的記憶體位址與對象B的記憶體位址不同,它們内部的引用類型屬性還都是相同的。
顯然:這種淺克隆的方式克隆出來的對象,不一定是我們想要的那種對象,是以有興趣的同學接着學習下面的深克隆。
深克隆
一、在淺克隆的基礎上,再次重寫clone方法,目的是不僅要克隆這個對象本身,我還要克隆這個對象中的引用類型屬性。
(你這時要跟從業人員說一下,我是來遊泳的,不是來洗腳的,你再給我加點水。)
@Override
protected Object clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
// 這裡對對象内的引用類型屬性進行克隆,使克隆更深入
person.birthday = (Date) person.birthday.clone();
return person;
}
二、直接Debug檢視再次重寫後的clone方法克隆的結果:

深克隆記憶體分析圖
結果分析:
1、person1與person2的記憶體位址跟淺克隆情況下的分析結果相同,也是不一樣的;
2、仔細看圖你會發現person1和person2的引用類型屬性birthday他們的記憶體位址值也不一樣啦,是以經過再次重寫後的clone方法我們稱之為深克隆。
(這下水就到腰了,可以愉快地遊泳啦。)
顯然:經過深克隆的方式克隆出來的對象,可能是我們想要的對象。
當然這隻是一個簡單的例子,讀者也可以找一些複雜的引用類型屬性(如:對象、集合等)來編寫測試案例玩一玩。
如果想深入學習,可以檢視一些實作了Cloneable接口的源碼的clone方法來進行學習,我相信你肯定不會去看,是以就當我沒說好啦。
很高興完成了這個部落格,有什麼寫不對的地方請您在下方留言指出。
如果您對深克隆和淺克隆的概念還是不大懂,那請您在下方留下您的支*寶賬号,将會有一筆巨額資金打入您的賬戶,讓我們一起學習慕*網的Java設計模式精講。
成長的路上,希望有你有我。