我使用apacheSerializationUtils
深度复制对象,发现了一些惊人的东西。例如,对象A
有两个成员B1
、B2
,并且它们都有相同的成员C
(引用同一对象(。在深度复制之后,创建了A'
,并且我期望B1'
具有成员C1'
,B2'
具有成员C2'
。但是出现了B1'
和B2'
都具有相同的成员C'
的情况。
似乎在深度复制之后,对象层次结构和关系得以保持。这是如何实现的?
如果两个引用指向同一个对象,那么我们将它们视为具有"相同标识"。也就是说,给定:
Object a = ...;
Object b = ...;
如果这一点成立,则a
和b
是"相同的":a == b
,仅当它们指向同一对象时才成立。
注意,a.equals(b)
是不同的;对其成立的任何两个引用都可以被视为"相等",但可能涉及2个对象。琐碎的例子:
String a = new String("Hello");
String b = new String("Hello");
a == b; // this is false
a.equals(b); // this is true
可以计算出两个引用是否相同,而不仅仅是相等。
一个简单的检查就是我刚刚向您展示的:==
,它检查相同和不相等。
最有可能的是,SerializationUnits中的代码使用WeakHashMap
,这是一个映射到标识(或多或少是"指针"(的映射。WHM主要是一个内部实现,但请注意,您总是可以通过System.identityHashCode
获得标识哈希代码,该代码为同一对象返回相同的值,即使该对象发生了变异。理论上,a.hashCode()
可以返回不同的值(对于可变对象,它往往会返回(,但对于VM的生命周期中的任何给定实例,System.identityHashCode(a)
都是相同的值。
Plain janeHashMap
使用a.hashCode()
来知道要查看哪个bucket,然后使用a.equals(b)
来扫描是否相等。
WeakHashMap
使用System.identityHashCode(a)
来知道要查看哪个bucket,然后使用a == b
来扫描相等性。
有了这一点,编写一个保持层次结构和关系的序列化程序就变得微不足道了。
还要注意,如果没有这样的机制,实体序列化是不可能的。毕竟,想象一下这个结构:
List<Object> list = new ArrayList<Object>();
list.add(list); // ooooh, recursion!
如果没有像WeakHashMap
这样的工具,任何序列化此构造的尝试都将导致StackOverflowError
,原因很明显。
我不知道Apache库,但很可能它保存了迄今为止复制的实例映射。如果遇到要复制的实例C
,则首先检查该实例的副本C'
是否已经存在。如果是,则使用现有副本。如果没有,它将创建C
的深度副本,给出C'
,并将该副本存储在映射中。
需要考虑的一点是:我想Apache的存在性测试基于==
运算符,而不是equals()
方法,因为==
运算符将给出最干净的结果,最类似于原始引用结构。如果不是,恰好满足equals()
测试的两个不同实例C1
和C2
将最终成为单个副本C'
。