【问题标题】:Java Custom SerializationJava 自定义序列化
【发布时间】:2011-11-09 14:02:51
【问题描述】:

我有一个对象,其中包含一些我想要序列化的不可序列化字段。它们来自一个我无法更改的单独 API,因此不能将它们设为 Serializable。主要问题是 Location 类。它包含四个我需要的可以序列化的东西,都是整数。如何使用 read/writeObject 来创建可以执行以下操作的自定义序列化方法:

// writeObject:
List<Integer> loc = new ArrayList<Integer>();
loc.add(location.x);
loc.add(location.y);
loc.add(location.z);
loc.add(location.uid);
// ... serialization code

// readObject:
List<Integer> loc = deserialize(); // Replace with real deserialization
location = new Location(loc.get(0), loc.get(1), loc.get(2), loc.get(3));
// ... more code

我该怎么做?

【问题讨论】:

  • 请注意,最好接受最佳答案而不是第一个正确答案。你自己说彼得劳里的答案可能会更好。为了所有未来来到这里寻找最佳答案的人:请考虑接受不同的答案!

标签: java serialization


【解决方案1】:

Java 支持Custom Serialization。阅读自定义默认协议部分。

引用:

然而,有一个奇怪而巧妙的解决方案。通过使用内置 序列化机制的特点,开发者可以增强 通过在其类文件中提供两种方法来正常处理。 这些方法是:

  • private void writeObject(ObjectOutputStream out) 抛出 异常;
  • private void readObject(ObjectInputStream in) 抛出 IOException,类NotFoundException;

在此方法中,如果需要,您可以将其序列化为其他形式,例如您说明的位置的 ArrayList 或 JSON 或其他数据格式/方法,然后在 readObject() 上重新构建它

在您的示例中,您添加以下代码:



private void writeObject(ObjectOutputStream oos)
throws IOException {
    // default serialization 
    oos.defaultWriteObject();
    // write the object
    List loc = new ArrayList();
    loc.add(location.x);
    loc.add(location.y);
    loc.add(location.z);
    loc.add(location.uid);
    oos.writeObject(loc);
}

private void readObject(ObjectInputStream ois)
throws ClassNotFoundException, IOException {
    // default deserialization
    ois.defaultReadObject();
    List loc = (List)ois.readObject(); // Replace with real deserialization
    location = new Location(loc.get(0), loc.get(1), loc.get(2), loc.get(3));
    // ... more code

}

【讨论】:

  • 好的,谢谢,这一切都很好。但据我所知,ArrayList 从未添加到writeObject 的序列化中。我该怎么做才能正确地将 List 添加到序列化中?
  • 我认为这不是真的。 ArrayList 实现可序列化。重要的是 ArrayList 的成员也是可序列化的。并且您的所有成员都是 int ,其 Integer (对象形式)是可序列化的。如果碰巧,我说的不是真的,那么你序列化为不同的形式,比如 JSON,或者如果你真的只需要四个整数,你甚至可以做一个 int1,int2,int3,int4 形式的字符串并在读。使用自定义序列化,您有许多选项来写入和检索数据。
  • 不,我明白这一点,但我似乎也需要做oos.writeObject(loc) 我可以做ois.readObject(),你似乎省略了。
  • 哦,是的,你是对的,我想念 :) 抱歉,我打字太匆忙了
  • 您不必有费尽心思将事物包装在 ArrayList 或 JSON 对象或其他任何东西中。 oos.writeInt(x); oos.writeInt(y); ... 和相应的输入法可以按照相同的顺序进行。
【解决方案2】:

类似于@momo 的答案,但不使用 List 和自动装箱的 int 值,这将使其更加紧凑。

private void writeObject(ObjectOutputStream oos) throws IOException {
    // default serialization 
    oos.defaultWriteObject();
    // write the object
    oos.writeInt(location.x);
    oos.writeInt(location.y);
    oos.writeInt(location.z);
    oos.writeInt(location.uid);
}

private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
    // default deserialization
    ois.defaultReadObject();
    location = new Location(ois.readInt(), ois.readInt(), ois.readInt(), ois.readInt());
    // ... more code

}

【讨论】:

  • 谢谢,这是一个很好的观点。然而,在我的实际代码中,我使用的不仅仅是一个数组,所以我使用了writeObject。我接受了 momo 的回答,因为它是第一个,但是对于给定的情况,这可能是一个更好的答案。
【解决方案3】:

如果必须是 Java 序列化,我知道的唯一方法是在所有引用 Location 实例的类中重新定义 readObject() 和 writeObject(),如 Momo 的回答所示。请注意,这将不允许您序列化 Location[],并要求您对代码中出现的所有 Collection&lt;Location&gt; 进行子类化。此外,它要求将Location 类型的字段标记为瞬态,这将排除它们的定义写入序列化流,这可能会阻止对不兼容的类更改的检测。

更好的方法是简单地覆盖ObjectOutputStream.writeObject。唉,那个方法是final。您可以改写ObjectOutputStream.writeObjectOverride(),但该方法不能委托默认实现ObjectOutputStream.writeObject0(),因为该方法是private。当然,你可以使用反射调用私有方法,但是...

因此,我建议验证您的约束条件。它必须是Java序列化吗?真的不能改Location类的定义吗?

如果您有 Location 类的源代码,添加 implements Serializable 并将其添加到您的类路径非常简单。是的,每次升级库时都必须再次执行此操作,但它可能比替代方案更好......

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-11
    相关资源
    最近更新 更多