版权声明: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() 方法就是一次浅拷贝的操做。