可以说我有一个出于某种原因需要处理序列化对象的旧版本的程序。
例如:反序列化时,可能会遇到以下版本之一。
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在这种情况下它将被初始化为其默认值。
serialVersionUID
InvalidClassException
boolean sharpTeeth;
sharpTeeth
false
对于要尝试同时处理向前和向后兼容性的分布式系统,这尤其重要。您不想升级应用程序A的版本并破坏依赖于A的另一个应用程序B。通过不更改serialVersionUID而是仅添加或删除字段就可以做到。实体的较高版本需要支持较旧的版本,而在较新的字段中没有值,但是较旧的实体不会介意新字段是否可用。这也意味着您也不应更改字段的比例。
序列化非常聪明,但是它不能处理字段的类型更改。您不应该只是paws从更改int为long。相反,我建议添加一个long pawsLong或多个这样的代码,然后编写代码来处理存在int paws或long pawsLong具有价值的可能性。
paws
int
long
long pawsLong
int paws
public long getPaws() { if (pawsLong > 0) { return pawsLong; } else { // paws used to be an integer return paws; } }
您还可以编写自己的readObject方法在反序列化时进行转换:
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 定义自定义和方法。就像是:
readObject(...)
writeObject(...)
// 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的序列化输入。
我不会建议任何这些方法。听起来很难维护。