我阅读了各种关于序列化和 serialVersionUID 使用的博客。他们中的大多数都提到使用它来维护可序列化类的状态。
我的场景是;
我知道旧的 serialVersionUID 和新的 serialVersionUID。
在读取具有旧 serialVersionUID 的对象时,我想操作数据以使其适合新版本,如果我正在读取的对象是旧类型,我只想费心去做。
这看起来应该是非常直接的事情!
有没有办法在读入对象时获取 serialVersionUID?
InvalidClassException 在调用序列化类中的 readObject 方法之前抛出,因此我无法在那里访问它。
我发现的唯一提示是覆盖 ObjectInputStream 以便 readClassDescriptor() 可用,尽管这似乎是解决常见问题的重量级解决方案!
感谢所有帮助!
中号
最佳答案
我将介绍几种在序列化中支持旧版本类/接口(interface)的可能方法:
使用迁移器
在这种情况下,您的 Java 项目中需要以下内容:
- 统一所有这些类的不可变接口(interface)
- 该接口(interface)的旧实现被注释为
@Deprecated
- 界面的新实现
- 一个迁移器类,可帮助您将已弃用的对象转换为新的对象
让我从一开始就说,并不总是可以使用旧版本的对象(请参阅 versioning of serializable objects 上的 Oracle 文档)。为了简单起见,让我们假设在升级时,您的类总是实现您定义的接口(interface) IEntity
。
public interface IEntity extends Serializable {
// your method definitions
}
并假设您最初使用该类:
public class Entity implements IEntity {
private static final long serialVersionUID = 123456789L;
// fields and methods
}
如果您需要将类 Entity
升级为具有新 serialVersionUID 的新实现,则首先将其注释为 @Deprecated
但不要重命名并且不要将其移动到另一个包 :
@Deprecated
public class Entity implements IEntity {
private static final long serialVersionUID = 123456789L;
// fields and methods
}
现在使用一个新的 serialVersionUID(重要)和一个额外的构造函数创建您的新实现,如下所示...
public class Entity_new implements IEntity {
private static final long serialVersionUID = 5555558L;
public Entity_new(IEntity){
// Create a new instance of Entity_new copying the given IEntity
}
}
当然,整个过程中最关键的部分是如何实现上面的构造函数。如果您已经将旧类型 Entity
的一些对象序列化为二进制文件(例如使用 ObjectOutputStream
)并且您现在已经迁移到 Entity_new
您可以将它们解析为 Entity
的实例,然后将它们转换为 Entity_new
的实例。这是一个例子:
public class Migrator {
private final IEntity entity;
private Class<? extends IEntity> newestClass = Entity_new.class;
public Migrator(final IEntity entity){
this.entity = entity;
}
public Migrator setNewestClass(Class<? extends IEntity> clazz){
this.newestClass = clazz;
return this;
}
public IEntity migrate() throws Exception {
Constructor<? extends IEntity> constr =
newestClass.getConstructor(IEntity.class);
return constr.newInstance(this.entity);
}
}
当然还有其他替代方案不需要特定的构造函数或使用 java reflection 。还有许多其他设计方法可供选择。另请注意,为简化上述代码,已完全省略异常处理和空对象检查。
设计一个通用的可序列化接口(interface)
如果适用,您可以首先尝试为您的类设计一个将来不太可能更改的接口(interface)。如果你的类需要存储一组极有可能被修改的属性,可以考虑为此使用一个Map<String, Object>
,其中String
指的是属性名/标识符,Object
是对应的值。
自定义 readObject 和 writeObject
还有另一种方法可以为旧版本提供支持,为了完整性我会提到它,但我不会选择这种方法。您可以以适应类/接口(interface)的当前和所有先前版本的方式实现 private void readObject(ObjectInputStream in)
和 private void writeObject(ObjectOutputStream out)
。我不确定这样的事情是否总是可行和可持续的,您最终可能会非常困惑和冗长地实现这些方法。
替代序列化技术
这并没有回答 OP 的问题,但我认为值得提出来。您可以考虑以某种 ASCII 格式(例如 JSON、YAML 或 XML)序列化您的对象。在这种情况下,除非您大力重新设计可序列化接口(interface),否则可扩展性是开箱即用的。如果您正在寻找可扩展的二进制协议(protocol),BSON(二进制 JSON)是一个不错的选择。也许这是在可能无法用 Java 实现的软件之间提供对象可移植性的最佳方式。
关于java - 升级 Java Serializable 类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5005330/