1.前言在任何编程语言中,其实都有浅拷贝和深拷贝的概念,Java也不例外。复制现有对象时,有浅拷贝和深拷贝。它们在实际使用中有很大的不同。如果他们混淆了,可能会出现一些难以排查的问题。本文对Java中的深拷贝和浅拷贝进行了详解。2、什么是浅拷贝和深拷贝?首先你要明白,浅拷贝和深拷贝都是对一个已有对象的操作。我们先来看看浅拷贝和深拷贝的概念。在Java中,除了基本数据类型(元类型)之外,还有一种称为类实例对象的引用数据类型。通常,“=”号用于赋值操作。对于基本数据类型,实际上是复制了它的值,但是对于对象,实际上只是赋值了这个对象的引用。当原始对象的引用被传递时,它们实际上仍然指向同一个对象。浅拷贝和深拷贝的区别就是建立在这个基础上的。如果复制这个对象时,只复制基本数据类型,而引用数据类型只是引用传递,没有真正创建。新对象被认为是浅拷贝。反之,复制引用数据类型时,会创建一个新的对象,并复制其中的成员变量,这被认为是深复制。那么到这里,你应该明白了,所谓的浅拷贝和深拷贝只是在复制对象时,对类的实例对象的引用数据类型进行了不同的操作。总结一下:1.浅拷贝:基本数据类型按值传递,引用数据类型按引用复制,这就是浅拷贝。2、深拷贝:传递基本数据类型的值,为引用数据类型新建一个对象,并复制其内容,为深拷贝。3.Java中的clone()3.1Object上的clone()方法在Java中,所有的类都继承自Object,而在Object上,有一个clone()方法,声明为protected,所以我们可以在它的子类中,使用它。无论是浅拷贝还是深拷贝,都需要实现clone()方法来完成操作。可见其实现非常简单。它限制所有调用clone()方法的对象实现Cloneable接口,否则会抛出异常CloneNotSupportedException。最后会调用internalClone()方法完成具体的操作。internalClone()方法实际上是一个本地方法。这个我们不用深究,只需要知道它可以clone()一个对象,得到一个新的对象实例即可。对比Cloneable接口,可以看出它不需要实现任何方法。可以简单理解为只是一个标记,允许复制这个对象的是开发者。3.2浅拷贝先来看一下浅拷贝的例子。首先创建一个名为FatherClass的类,实现Cloneable接口,重写clone()方法。然后先正常new一个FatherClass对象,然后用clone()方法新建一个对象。***看输出Log:I/cxmyDev:fatherA==fatherB:falseI/cxmyDev:fatherAhash:560973324I/cxmyDev:fatherBhash:560938740I/cxmyDev:fatherAname:ZhangSanI/cxmyDev:fatherBname:ZhangSan可以看到,使用clone()方法,从==和hashCode的区别可以看出,clone()方法实际上是创建了一个新的对象。但这只是一个浅拷贝操作。为了验证这一点,继续往下看,在FatherClass中,也有一个ChildClass的对象child,clone()方法也能正常复制吗?重写上面的Demo。看,里面的child就是负责这里的,用它来看输出Log效果。I/cxmyDev:fatherA==fatherB:falseI/cxmyDev:fatherAhash:560975188I/cxmyDev:fatherBhash:560872384I/cxmyDev:fatherAname:张三I/cxmyDev:fatherBname:张三I/cxmyDev:==================I/cxmyDev:A.child==B.child:trueI/cxmyDev:fatherA.childhash:560891436I/cxmyDev:fatherB.childhash:560891436从***到child的输出可以看出,A和B的子对象实际上仍然指向同一个对象,只是传递了对它的引用。3.3深拷贝既然了解了clone()方法,那么只能对当前对象进行浅拷贝,引用类型还是传引用。那么,如何进行深拷贝呢?常见的解决方法有两种:序列化(serialization)这个对象,然后反序列化回来,就可以得到这个新的对象,无非就是序列化规则需要自己写。继续使用clone()方法。由于clone()方法是我们重写的,所以我们其实可以把里面的引用类型变量clone()一遍。继续改写上面的Demo,让ChildClass也实现Cloneable接口。最重要的代码在FatherClass.clone()中,对里面的child进行clone()操作。再看输出Log。I/cxmyDev:fatherA==fatherB:falseI/cxmyDev:fatherAhash:561056732I/cxmyDev:fatherBhash:561057344I/cxmyDev:fatherAname:张三I/cxmyDev:fatherBname:张三I/cxmyDev:==================I/cxmyDev:A.child==B.child:falseI/cxmyDev:fatherA.childhash:561057304I/cxmyDev:fatherB.childhash:561057360可以看到child也是一个副本made,其实就是ChildClass的浅拷贝,而对于FatherClass来说,就是深拷贝。其实深拷贝的思路也是类似的,无论是序列化还是使用clone(),其实都是需要我们自己编写拷贝的规则,最终实现深拷贝的目的。如果要实现深拷贝,推荐使用clone()方法,这样每个类只需要维护自己,不需要关心其他内部对象中的其他参数是否也需要clone().4.小结到此为止,Java中的浅拷贝和深拷贝的概念已经基本梳理清楚了。其实浅拷贝和深拷贝只是相对的。如果一个对象内部只有基本数据类型,那么使用clone()方法获取对象的深拷贝,如果内部有引用数据类型,那么使用clone()方法是浅拷贝操作。【本文为专栏作家“张扬”原创稿件,转载请微信公众号联系作者获取授权】点此查看该作者更多好文
