当前位置: 首页 > 面试题库 >

管理序列化Java对象的多个版本

壤驷华美
2023-03-14
问题内容

可以说我有一个出于某种原因需要处理序列化对象的旧版本的程序。

例如:反序列化时,可能会遇到以下版本之一。

class Pet {
    private static final long serialVersionUID = 1L;
    int paws;
}

class Pet {
    private static final long serialVersionUID = 2L;
    long paws; // handle marsian centipedes
    boolean sharpTeeth;
}

让我们假设可以使用某种巧妙的策略(设置不存在的字段等)将(旧的)对象转换为新的对象,但是:

如何安排我的源代码?编写转换器时,我可能需要在同一源代码树中同时使用这两个版本,但是如何在eclipse中进行处理。

我应该在一个类加载器中进行反序列化吗?如果失败,请尝试使用另一个使用旧版本(依此类推)的类加载器,还是有更好的方法?

最好的策略是什么?


问题答案:

让我们假设可以使用某种巧妙的策略(设置不存在的字段等)(逻辑上)将旧对象转换为新对象…如何布置源代码?

我看到两种处理方式。首先,serialVersionUID除非您想InvalidClassException抛出该异常,否则切勿更改该异常。第二条规则是
更改字段的 类型 ,而仅添加或删除序列化自动处理的字段。例如,如果序列化的文件具有该类的版本,boolean sharpTeeth;但该类没有该字段,则在反序列化期间它将被忽略。如果反序列化的类具有该sharpTeeth字段,但文件没有,则false在这种情况下它将被初始化为其默认值。

对于要尝试同时处理向前和向后兼容性的分布式系统,这尤其重要。您不想升级应用程序A的版本并破坏依赖于A的另一个应用程序B。通过不更改serialVersionUID而是仅添加或删除字段就可以做到。实体的较高版本需要支持较旧的版本,而在较新的字段中没有值,但是较旧的实体不会介意新字段是否可用。这也意味着您也不应更改字段的比例。

序列化非常聪明,但是它不能处理字段的类型更改。您不应该只是paws从更改intlong。相反,我建议添加一个long pawsLong或多个这样的代码,然后编写代码来处理存在int pawslong pawsLong具有价值的可能性。

public long getPaws() {
    if (pawsLong > 0) {
        return pawsLong;
    } else {
        // paws used to be an integer
        return paws;
    }
}

您还可以编写自己的readObject方法在反序列化时进行转换:

private void readObject(java.io.ObjectInputStream in) {
    super.readObject(in);
    // paws used to be an integer
    if (pawsLong == 0 && paws != 0) {
        pawsLong = paws;
    }
}

如果这对您不起作用,那么自定义序列化是解决之道。但是,您必须从头开始,readObject(...)writeObject(...)使用内部版本ID
定义自定义和方法。就像是:

// never change this
private static final long serialVersionUID = 3375159358757648792L;
// only goes up
private static final int INTERNAL_VERSION_ID = 2;
...
// NOTE: in version #1, this was an int
private long paws;

private void readObject(java.io.ObjectInputStream in) {
    int version = in.readInt();
    switch (version) {
        case 1 :
            paws = in.readInt();
            ...
        case 2 :
            paws = in.readLong();
            ...

private void writeObject(java.io.ObjectOutputStream out) {
    out.writeInt(INTERNAL_VERSION_ID);
    out.writeLong(paws);
    ...

但是此方法不能帮助您实现向前兼容性。版本1的读者不会理解版本2的序列化输入。

我应该在一个类加载器中进行反序列化吗?如果失败,请尝试使用另一个使用旧版本(依此类推)的类加载器,还是有更好的方法?

我不会建议任何这些方法。听起来很难维护。



 类似资料:
  • 问题内容: 你好亲爱的同事们, 我有一个Garden类,在其中我可以序列化和反序列化多个Plant类对象。如果想将其分配给mein静态方法中的调用变量,则可以进行序列化,但是不能进行反序列化。 反序列化代码: 我的调用代码: 编辑 我有一个空的Pointer异常 @NilsH的解决方案工作正常,谢谢! 问题答案: 如何序列化整个列表呢?无需序列化列表中的每个对象。 如果那不能解决您的问题,请发布有

  • 问题内容: 我可能会尝试以困难的方式执行此操作,所以请让我知道是否有更好的解决方案。 我正在用Java开发一个简单的文字游戏,您可以通过GUI选择动作。我有几个班级正在尝试序列化一个是播放器,另一个是NPC。是否有一种简单的方法可以将一个以上的对象(播放器和NPC)序列化到同一文件中?我可以序列化一个对象并将其加载回游戏中。 我会以错误的方式处理吗?有没有更简单的方法来尝试保存游戏状态? 如果我有

  • 问题内容: 我问了一个先前的问题,关于如何将多个保存文件保存到.ser文件中,建议我使用哈希集,因为它们是可序列化的。我试图编写一段测试代码来序列化哈希集内的每条信息: 但是我在获取代码来编写带有index(i)的对象时遇到了麻烦,因为它说未为type定义该方法。有没有一种方法可以遍历哈希集中的每条信息,还是应该一次序列化整个集合。另一个问题是,如果第一个答案是后者,我将如何将数据反序列化为单独的

  • 我尝试反序列化json: 我有两个实体: 和 如何使用一个json条目同时反序列化两个实体?以下是使用Jackson ObjectMapper的main摘录: 它解析日期、uuid和数据集,但不能解析具有hmm_subfamily、hmm_evalue和hmm_score值的HmmResult对象:我得到错误:p.getHmmResult():null。(HmmResult hm=ObjMappe

  • 本文向大家介绍java对象的序列化和反序列化,包括了java对象的序列化和反序列化的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了java对象的序列化和反序列化,供大家参考,具体内容如下 1. 什么是序列化        将对象转换为字节流保存起来,比如保存到文件里,并在以后还原这个对象,这种机制叫做对象序列化。(补充一句:把对象保存到永久存储设备上称为持久化) 2. 怎么实现序列化

  • 错误: java.lang.ClassNotFoundException:testprocedure.tp$3在java.net.URLClassLoader$1上运行(未知源)在java.net.URLClassLoader上运行(未知源)在java.security.accessController.doprivileged(本机方法)在java.net.URLClassLoader.find