为其他对象实现与String.intern()等效的对象 [英] Implementing an equivalent to String.intern() for other objects
问题描述
我正在尝试实现与String.intern()等效的方法,但对于其他对象. 我的目标是: 我有一个对象A,将对其进行序列化然后反序列化. 如果某处还有另一个对A的引用,我希望反序列化的结果是相同的引用.
I'm trying to implement an equivalent to String.intern(), but for other objets. My goal is the following: I've an object A which I will serialize and then deserialize. If there is another reference to A somewhere, I want the result of the deserialization to be the same reference.
这是我期望的一个例子.
Here is one example of what I would expect.
MyObject A = new MyObject();
A.data1 = 1;
A.data2 = 2;
byte[] serialized = serialize(A);
A.data1 = 3;
MyObject B = deserialize(serialized); // B!=A and B.data1=1, B.data2=2
MyObject C = B.intern(); // Here we should have C == A. Consequently C.data1=3 AND C.data2=2
这是我的实现atm. (MyObject
类扩展了InternableObject
)
Here is my implementation atm. (the MyObject
class extends InternableObject
)
public abstract class InternableObject {
private static final AtomicLong maxObjectId = new AtomicLong();
private static final Map<Long, InternableObject> dataMap = new ConcurrentHashMap<>();
private final long objectId;
public InternableObject() {
this.objectId = maxObjectId.incrementAndGet();
dataMap.put(this.objectId, this);
}
@Override
protected void finalize() throws Throwable {
super.finalize();
dataMap.remove(this.objectId);
}
public final InternableObject intern() {
return intern(this);
}
public static InternableObject intern(InternableObject o) {
InternableObject r = dataMap.get(o.objectId);
if (r == null) {
throw new IllegalStateException();
} else {
return r;
}
}
}
我的单元测试(失败):
My unit test (which fails):
private static class MyData extends InternableObject implements Serializable {
public int data;
public MyData(int data) {
this.data = data;
}
}
@Test
public void testIntern() throws Exception {
MyData data1 = new MyData(7);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(data1);
oos.flush();
baos.flush();
oos.close();
baos.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
MyData data2 = (MyData) ois.readObject();
Assert.assertTrue(data1 == data2.intern()); // Fails here
}
失败是由于以下事实:在反序列化时,将调用InternableObject的构造函数,因此objectId将为2(即使序列化数据包含"1")
The failure is due to the fact that, when deserializing, the constructor of InternableObject is called, and thus objectId will be 2 (even if the serialized data contains "1")
关于如何解决此特定问题的任何想法,或者另一种解决高级问题的方法?
Any idea about how to solve this particular problem or, another approach to handle the high level problem ?
谢谢大家
推荐答案
请勿使用构造函数创建实例.使用工厂方法来首先检查实例是否已经存在,如果没有匹配的实例,则仅创建一个实例.
Do not use the constructor to create instances. Use a factory method that checks if an instance already exists first, only create an instance if there isn't already a matching one.
要获得序列化的配合,您的类将需要使用readResolve()/writeReplace(). http://docs.oracle.com /javase/7/docs/platform/serialization/spec/serial-arch.html#4539
To get serialization to cooperate, your class will need to make use of readResolve() / writeReplace(). http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serial-arch.html#4539
实现构造函数的方式是在构造过程中泄漏引用,这可能导致很难确定问题.另外,您的实例映射不受任何锁的保护,因此它也不是线程保存的.
The way you implemented your constructor, you're leaking a reference during construction, which can lead to very hard to nail down problems. Also, your instance map isn't protected by any locks, so its not thread save.
这篇关于为其他对象实现与String.intern()等效的对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!