在最近的秋招中,阿里和多益網絡都問到了這個問題,雖然很簡單,可是我仍是想總結一下,感興趣的能夠看一下個人我的博客網站(Spring+MyBatis+redis+nginx+mysql)(適合菜鳥),最近會抽空把最近面試遇到的問題總結一下。java
本文針對問題:深克隆和淺克隆的區別和實現方式?(阿里電面,多益網絡的選擇題)mysql
最近不止一次碰見深淺克隆(深複製,淺複製)的問題,除了印象中有個clone方法外一臉懵逼!!!克隆(複製)在Java中是一種常見的操做,目的是快速獲取一個對象副本。克隆分爲深克隆和淺克隆。nginx
淺克隆:建立一個新對象,新對象的屬性和原來對象徹底相同,對於非基本類型屬性,仍指向原有屬性所指向的對象的內存地址。面試
深克隆:建立一個新對象,屬性中引用的其餘對象也會被克隆,再也不指向原有對象地址。redis
總之深淺克隆都會在堆中新分配一塊區域,區別在於對象屬性引用的對象是否須要進行克隆(遞歸性的)。sql
pos:當前對象的地址;cookie
son:son屬性所指向的地址;網絡
name:對象的name屬性。ide
case1:測試
public class Son implements Serializable , Cloneable{ private String name; private Son son; public Son() { super(); } public String getName() { return name; } public void setName(String name) { this.name = name; } public Son getSon() { return son; } public void setSon(Son son) { this.son = son; } @Override public String toString() { return super.toString(); } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
測試
public static void main(String[] args) throws Exception{ // 建立父親(LiLiu),兒子(LiWu),孫子(LiLiu)並關聯 Son father = new Son(); father.setName("LiSi"); Son son = new Son(); son.setName("LiWu"); Son grandSon = new Son(); grandSon.setName("LiLiu"); father.setSon(son); son.setSon(grandSon); // 調用clone方法 Son fatherCopy = (Son) father.clone(); boolean flag1 = fatherCopy==father; boolean flag2 = fatherCopy.getSon() == son; boolean flag3 = fatherCopy.getSon().getSon() == grandSon; // 比較克隆後的地址 System.out.println(flag1);// false System.out.println(flag2);// true System.out.println(flag3);// true // 比較Name flag1= fatherCopy.getName()==father.getName(); flag2 = fatherCopy.getSon().getName() == son.getName(); flag3 = fatherCopy.getSon().getSon().getName() == grandSon.getName(); System.out.println(flag1);// true System.out.println(flag2);// true System.out.println(flag3);// true //將對象寫到流裏 ByteArrayOutputStream byteOut=new ByteArrayOutputStream(); ObjectOutputStream objOut=new ObjectOutputStream(byteOut); objOut.writeObject(father); //從流裏讀出來 ByteArrayInputStream byteIn=new ByteArrayInputStream(byteOut.toByteArray()); ObjectInputStream objInput=new ObjectInputStream(byteIn); fatherCopy = (Son) objInput.readObject(); flag1= fatherCopy==father; flag2 = fatherCopy.getSon() == son; flag3 = fatherCopy.getSon().getSon() == grandSon; System.out.println(flag1);// false System.out.println(flag2);// false System.out.println(flag3);// false // 比較Name flag1= fatherCopy.getName()==father.getName(); flag2 = fatherCopy.getSon().getName() == son.getName(); flag3 = fatherCopy.getSon().getSon().getName() == grandSon.getName(); System.out.println(flag1);// false System.out.println(flag2);// false System.out.println(flag3);// false }
從上文代碼及運行結果不難看出,若是對象實現Cloneable並重寫clone方法不進行任何操做時,調用clone是進行的淺克隆。而使用對象流將對象寫入流而後再讀出是進行的深克隆。
思考:既然實現Cloneable接口並重寫clone接口只能進行淺克隆。可是若是類的引用類型屬性(以及屬性的引用類型屬性)都進行淺克隆,直到沒有引用類型屬性或者引用類型屬性爲null時,總體上就造成了深克隆。既對象的引用類型屬性和屬性的應用類型屬性都實現Coloneable,重寫clone方法並在clone方法中進行調用。
protected Object clone() throws CloneNotSupportedException { Son result = (Son) super.clone(); if (son != null) { result.son = (Son) son.clone(); } return result; }
我的認爲,在選擇深克隆方法時,應根據對象的複雜成都,如引用類型屬性是否有多層引用類型屬性關係。若是對象只有一層或者兩層引用類型的屬性,選擇思考中所提到的方法較爲方便,反之則使用對象流。