在最近的秋招中,阿里和多益网络都问到了这个问题,虽然很简单,可是我仍是想总结一下,感兴趣的能够看一下个人我的博客网站(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; }
我的认为,在选择深克隆方法时,应根据对象的复杂成都,如引用类型属性是否有多层引用类型属性关系。若是对象只有一层或者两层引用类型的属性,选择思考中所提到的方法较为方便,反之则使用对象流。