转载

再谈 Java 深浅拷贝

去年的一篇文章总结了一下深浅拷贝,Immutable 和 unmodifiable 这三个概念,今天再看看 Java 的深浅拷贝。

考虑以下代码:

static class ImmutablePerson {     final String mName;      ImmutablePerson(String name) {         mName = name;     } }  List<ImmutablePerson> list1 = new ArrayList<>(); list1.add(new ImmutablePerson("Piasy")); List<ImmutablePerson> list2 = new ArrayList<>(list1); 

list2 = new ArrayList<>(list1) 是否是一次深拷贝呢?实际情况是这样的:

  • list2list1 是两个不同的对象,为 list2 增加一个元素,并不会导致 list1 也增加一个元素
  • 但是 list2 内的元素,和 list1 内的元素,也就是它俩唯一的一个元素 Person 对象,是同一个对象,内存地址都是一样的
  • 查看 ArrayList 的构造函数,我们发现它通过 System.arraycopy 来拷贝传入的数据,这说明 System.arraycopy 进行的是浅拷贝 ,而 这个测试用例 的结果也同样证明了这个结论
  • 但是由于 Person 类的 mName 声明为了 final ,而 String 又是 immutable 的,所以实际上, Person 类就是 immutable 的,因此,这里 System.arraycopy 进行的浅拷贝不会带来任何问题
  • 但是必须注意,一旦 Person 类不是 immutable 的,那这里就可能会产生 bug 了,例如, list1.get(0).mName = "Test" ,就会导致 list2.get(0).mName 也变成了 Test , 这个测试用例 的结果证明了这个结论

所以去年的文章中描述的深拷贝并不严谨,实际上还是浅拷贝。那么 Java 里面什么才是深拷贝呢?(实现正确的) serialization!为什么需要说是实现正确的呢?因为只有在 clone 方法中 new 了一个新对象,并且对于每个成员,都是 new 了一个新对象再赋值,这才是彻彻底底的深拷贝,没有任何一个成员变量是以前的副本的成员变量的浅拷贝。 这个测例 实际上就不是一个实现正确的 serialization。

但这样彻底的深拷贝开销太大,所以并不实用,更好的做法还是 Immutable,一旦实现了 Immutable,浅拷贝反倒是最好的搭档了,既高效又安全。而为了降低开销,并且保证语义的准确,最好的实践还是用 unmodifiable 来实现 immutable。

  • ← Previous Post
原文  http://blog.piasy.com/2016/03/31/Java-Defensive-Copy-Immutable-Unmodifiable-2/
正文到此结束
Loading...