Java中的浅拷贝和深拷贝
引言
最近在复审代码时,发现组内一个同学写了这么一段代码:
1 | public void process(ResultContext rc){ |
这段代码,本意是想从同一个 ResultContext
获取到原始数据,然后对这份数据做不同的加工,再分别保存。但这段代码实际上是有问题的。因为 m1
和 m2
虽然是不同的引用,但实际上都指向了同一个对象,在修改 m1
的同时, m2
实际上也发生了改变,反之亦然。
Java 中的浅拷贝(shallow copy)
在 java 中,如果简单地对一个对象进行赋值,那就是浅拷贝。如下面的例子, m1
和 m2
都是指向同一个 Map 容器的引用,其中一个改了,另一个也跟着改。
1 | Map m1 = rc.getResult(); |
如果你的 Map 存储的是基本数据类型或String,可以用 putAll
方法,来避免这种情况。
1 | private void process(Map<String,String> map){ |
但假设你的 Map 存储的是引用对象类型,实际上 putAll 也是潜拷贝。因为虽然是不同的两个 map 容器,但 map 容器里存储的实际上是同一个对象,m1
里的对象改了, m2
里的对象也会改变。
1 | public static void main(String[] args) { |
Java 中的深拷贝(deep copy)
那如果希望将容器里面的内容也复制一份,就需要用到深拷贝。
jdk 并没有提供深拷贝的方法给我们使用,但 Obejct 类提供了 clone()
方法,clone()
方法是一个浅拷贝,我们可以重写该方法,实现自定义的深拷贝逻辑,基本思想就是在拷贝的时候对容器里的所有对象都进行 new 。
当然,也可以借助一些第三方库,如 Apache Commons Lang
库里有一个 SerializationUtils#clone
方法,但需要你的对象实现 serializable
接口。
1 | Map deepCopyMap = (Map) SerializationUtils.clone(map); |
还有一种方式是通过 Gson
或 jackson
等框架将对象序列化后再反序列化到新的对象。