我正在使用Java Serializable
接口和ObjectOutputStream
来序列化对象(到目前为止,这个方法已经足够我的目的了。)
我的API依赖于某些操作的对象标识,我想知道它是否会被序列化保留。也就是说:如果,对于两个任意对象a
和b
,它在序列化之前保持a == b
,它在反序列化后是否仍然保持?
我发现了claim the contrary的一些文本 - 但他们要么写了一个旧版本的JRE(我只对1.6感兴趣,也许是1.5),或者关注RMI(这是不相关的)对我来说。)
关于对象身份,documentation并不是很明确。 sun.com上的technical article提到ObjectOutputStream
使用对象缓存,这对我来说只有在确实保留了对象标识时才有意义,但我没有足够的信心依赖这些脆弱的证据。 / p>
我已经尝试过(Java 1.6,OS X)并发现是,对象的标识在序列化中保持不变。但我可以从这些结果中推断出来还是不可靠?
对于我的测试,我已经序列化了以下对象图:
C----------+
| b1 b2 |
+----------+
| |
v v
B---+ B---+
| a | | a |
+---+ +---+
\ /
\ /
\/
A----+
| |
+----+
最小的再现代码:
import java.io.*;
public class SerializeTest {
static class A implements Serializable {}
static class B implements Serializable {
final A a;
public B(A a) {
this.a = a;
}
}
static class C implements Serializable {
final B b1, b2;
public C() {
A object = new A();
b1 = b2 = new B(object);
}
}
public static void main(String[] args) throws IOException,
ClassNotFoundException {
C before = new C();
System.out.print("Before: ");
System.out.println(before.b1.a == before.b2.a);
// Serialization.
ByteArrayOutputStream data = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(data);
out.writeObject(before);
out.close();
// Deserialization.
ObjectInputStream in =
new ObjectInputStream(new ByteArrayInputStream(data.toByteArray()));
C after = (C) in.readObject();
System.out.print("After: ");
System.out.println(after.b1.a == after.b2.a);
}
}
答案 0 :(得分:18)
对于两个任意对象a和b,如果它在序列化之前保持== b,则在反序列化之后它仍将保持为真:
readResolve()
,它有可能改变引用的恢复方式;持有a和b的课程也没有。对于所有其他情况,不会保留对象标识。
答案 1 :(得分:10)
答案是否,默认情况下,如果您正在考虑给定对象/图形的2个单独序列化,则不会通过序列化保留对象标识。例如,如果我通过线路序列化对象(也许我通过RMI将它从客户端发送到服务器),然后再次执行(在单独的RMI调用中),那么服务器上的2个反序列化对象将不是==。
然而,在“单一序列化”中,例如单个客户端 - 服务器消息,它是包含多次相同对象的图形,然后在反序列化时,保留标识 。
对于第一种情况,您可以提供readResolve
方法的实现,以确保返回正确的实例(例如,在 typesafe enum 模式中)。 readResolve
是一个私有方法,它将由JVM在反序列化的Java对象上调用,使对象有机会返回不同的实例。例如,这是在TimeUnit
添加到语言之前enum
enum
可能已实施的方式:
public class TimeUnit extends Serializable {
private int id;
public TimeUnit(int i) { id = i; }
public static TimeUnit SECONDS = new TimeUnit(0);
//Implement method and return the relevant static Instance
private Object readResolve() throws ObjectStreamException {
if (id == 0) return SECONDS;
else return this;
}
}