为其他对象实现与String.intern()等效的对象 [英] Implementing an equivalent to String.intern() for other objects

查看:100
本文介绍了为其他对象实现与String.intern()等效的对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现与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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆