作者:吴大山 来源:http://wudashan.com/2018/10/14/Java-Deep-Copy/
介绍
在Java语言中,当我们需要复制一个对象时,有两种类型的复制:浅复制和深复制。 浅拷贝只复制源对象的地址,因此当源对象的值发生变化时,复制对象的值也会发生变化。 深拷贝复制源对象的所有值,因此即使源对象的值发生变化,复制的对象的值也不会改变。 如下图所示:
了解了浅拷贝和深拷贝的区别后,本博客将教你几种深拷贝的方法。
复制对象
首先,让我们定义需要复制的简单对象。
如上面的代码,我们定义了一个User类,其中包含姓名和地址,这不是一个字符串,而是另一个类,其中包含国家和城市。 这里省略了构造函数和成员变量的get()和set()方法。 接下来我们将详细介绍如何深拷贝User对象。
方法构造器
我们可以通过调用构造函数来进行深拷贝。 如果形参是基本类型和字符串,则直接赋值,如果是对象,则新建。
测试用例
方法2重载clone()方法
父类有一个clone()复制方法,但它是一个类型,我们需要重写它,将其修改为类型。 另外,子类还需要实现接口来告诉JVM这个类可以被复制。
重写代码
我们来修改User类、类和实现接口来支持深拷贝。
需要注意的是,super.clone()实际上是浅拷贝,所以重写User类的clone()方法时,对象需要调用.clone()重新赋值。
测试用例
方法3 Lang序列化
Java提供了序列化的能力。 我们可以先序列化源对象,然后反序列化生成复制对象。 但是,使用序列化的前提是复制的类(包括其成员变量)需要实现接口。 Lang包封装了Java序列化,我们可以直接使用。
重写代码
我们来修改User类、类、实现接口来支持序列化。
测试用例
方法四 Gson序列化
Gson可以将对象序列化为JSON,也可以将JSON反序列化为对象,因此我们可以用它来进行深拷贝。
测试用例
方法五序列化
与Gson类似,对象可以序列化为JSON。 明显的区别是复制的类(包括其成员变量)需要有一个默认的无参数构造函数。
重写代码
让我们修改 User 类,并实现默认的无参构造函数来支持它。
测试用例
总结
说了这么多深拷贝的实现方法,到底哪种方法最好呢? 最简单的判断就是根据被复制的类(包括其成员变量)是否提供深拷贝构造函数、是否实现接口、是否实现接口、是否实现默认的无参构造函数来进行选择。 详细考虑见下表:
参考阅读
深拷贝实现代码: