一、前言
不管变成什么语言,其实都有浅拷贝和深拷贝的概念,Java也不例外。 复制操作现有对象时,有浅副本和深副本之分。 他们在实际使用中存在很大的差异,如果将其混淆,可能会引起难以排除的问题。
本文详细介绍了Java的深度拷贝和轻拷贝。
二、什么是浅拷贝和深拷贝
首先,必须了解浅拷贝和深拷贝都是对现有对象的操作。 首先,让我们来看看浅拷贝和深拷贝的概念。
除了基本数据类型(元类型)之外,Java还具有名为类的实例对象的引用数据类型。 一般来说,将“=”符号用于赋值操作时。 对于基本数据类型,实际上是复制的该值,但对于对象,实际上只有该对象的引用被赋值,即使传递了对原始对象的引用,实际上也指向同一对象。
浅副本和深副本据此加以区分。 复制此对象时,只复制基本数据类型,引用并传递引用数据类型;如果没有实际创建新对象,则视为浅副本。 相反,如果在复制引用数据类型时创建了新对象并复制了其中的成员变量,则将其视为深度复制。
所以现在应该理解,浅副本和深副本只是引用数据类型的不同操作,即在复制对象时类的实例对象。
总的来说:
1、浅拷贝:对基本数据类型进行传值,对引用数据类型进行类似于引用传递的拷贝。 这是浅拷贝。
2、深度复制:对基本数据类型进行传递,对引用数据类型创建新对象,并复制其内容。 这就是深拷贝。
三. Java的克隆(
3.1对象上的clone ()方法
在Java中,所有Class都是从Object继承的,但Object具有声明为protected的clone ()方法,因此可以在子类中使用。
无论是浅拷贝还是深拷贝,都必须实现clone ()方法才能完成操作。
此实现非常简单,因为它限制了调用clone (方法的所有对象都必须实现Cloneable接口,否则会抛出一个名为CloneNotSupportedException的异常最终,将调用internalClone ()方法以完成特定操作。 internalClone (方法实际上是朴素方法。 没有必要对此进行深入思考。 clone (只要知道对象可以获取新的对象实例就可以了。
相反,在Cloneable界面中,您会发现实际上不需要以任何方式来实现。 他能轻易理解的只是一个标记,允许开发人员复制这个对象。
3.2轻拷贝
我们先来看看浅拷贝的例子。
首先,将class创建为FatherClass,实现Cloneable接口,然后重写clone ()方法。
然后,在常规new上创建FatherClass对象,然后使用clone ()方法创建新对象。
请看最后输出的Log :
I/cxmydev : fathera==fatherb : false
I/cxmydev : fatherahash :560973324
I/cxmydev : fatherbhash :560938740
I/cxmyDev: fatherA name :糟糕的衬衫/pi/cxmydev : father bname :糟糕的衬衫/p显示,使用clone ()方法时,==和hashCode
但是,这是浅拷贝的操作。
请验证这个,继续看。 FatherClass还有另一个ChildClass的对象child。 clone (方法也可以正常复制吗? 重写上面的Demo。
你看,这里负责其中的child,用它来看看输出的Log效果吧。
I/cxmydev : fathera==fatherb : false
I/cxmydev : fatherahash :560975188
I/cxmydev : fatherbhash :560872384
I/cxmyDev: fatherA name :糟糕的衬衫/pi/cxmydev : father bname :糟糕的衬衫/pi/cxmydev :========
I/cxmydev : a.child==b.child : true
I/cxmy
Dev: fatherA.child hash : 560891436I/cxmyDev: fatherB.child hash : 560891436
从最后对 child 的输出可以看到,A 和 B 的 child 对象,实际上还是指向了统一个对象,只对对它的引用进行了传递。
3.3 深拷贝
既然已经了解了对 clone() 方法,只能对当前对象进行浅拷贝,引用类型依然是在传递引用。
那么,如何进行一个深拷贝呢?
比较常用的方案有两种:
序列化(serialization)这个对象,再反序列化回来,就可以得到这个新的对象,无非就是序列化的规则需要我们自己来写。
继续利用 clone() 方法,既然 clone() 方法,是我们来重写的,实际上我们可以对其内的引用类型的变量,再进行一次 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 : 糟糕的衬衫/p>
I/cxmyDev: fatherB name : 糟糕的衬衫/p>
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() 方法就是一次浅拷贝的操作。