我正在编写一个文档存储的java应用程序。我创建了我的对象,并通过序列化将它们保存到磁盘。
当我从磁盘加载对象时遇到错误,但实际上我已经更改了它序列化的基础对象。
这似乎是管理存储对象的一种不好的方法,如果我通过更改我的基础对象来更新我的软件,那么我在磁盘上的所有对象都将无效。
是否有处理此问题的指导或最佳做法?或者我有更好的方法来保存我的数据吗?
答案 0 :(得分:3)
您需要阅读Java Object Serialization Specification,特别是Compatible Java Type Evolution部分以及紧随其后的部分,类型更改会影响序列化。
第1.10节规定:
对于可序列化的对象,即使存在不同(但兼容)的类实现版本,也会保留足够的信息来恢复这些对象。
作为开发人员,您有责任确保对类的更改不会与早期的序列化版本冲突。它并不像你想象的那么难。大多数情况下,您需要避免incompatible changes:
static
字段;静态字段不是序列化的,因此就序列化而言,这相当于删除它。)您可以通过向班级添加void writeObject(ObjectOutputStream)
方法来保存其他数据,还可以通过添加void readObject(ObjectInputStream)
方法执行其他初始化。这些在documentation for Serializable中有详细描述。请注意,这些方法中的第一行代码应分别为stream.defaultWriteObject()
和stream.defaultReadObject()
。
readObject
很重要。例如,如果您有一个新字段,您始终希望它是非空的:
private List<String> names = new ArrayList<>();
任何没有该字段存在的序列化的旧实例将被反序列化,该字段完全取消设置 - 也就是说,它将保持为null(因为创建对象时所有Object字段都为null,除非显式初始化)。您可以使用readObject
来解释此问题:
private void readObject(ObjectInputStream stream)
throws IOException,
ClassNotFoundException {
// First, do default serialization
stream.defaultReadObject();
if (this.names == null) {
this.names = new ArrayList<>();
}
}
答案 1 :(得分:2)
根据您的存储要求,您可以选择(但不限于)(在我看来最难以实现):
使用嵌入式数据库并将这些文件存储在磁盘上。有很多选择可供选择。 Derby,HSQL,H2,Sqlite等等。那么优点是您可以使用Flyway之类的迁移工具。每当架构发生更改时,您都会编写脚本并确保在应用程序启动时运行该工具。它实际上非常简单,但事先不知道所有这些东西会使学习曲线变得更加陡峭。你也有很多灵活性。
序列化为JSON或XML。同样,有很多工具可供选择,但对于通用JSON序列化,我建议使用Jackson。 JSON可以被uglified(放在一行中没有空格和所有这些),以便它使用更少的磁盘空间。使用起来非常简单,但灵活性较差。例如,当您只向类中添加字段时,您不需要太在意,但是会更改层次结构或提取较小的类。然后你就自己做了。您需要考虑一种迁移数据的方法,这可能会让您感到不堪重负。
与上一个选项非常相似,您可以使用更多磁盘效率更高的解决方案,例如Avro。这具有已经提到的所有缺点。
你自己提出的任何事情。我不建议这样做。有可能,你会对你的解决方案感到满意,不过是因为它更简单,更容易理解,或者其他什么,但是很短的时间。然后,您必须支持更多代码,担心效率和性能(如果必须)以及您将不可避免地遇到的更多挑战。不要这样做,找一个你会得到帮助的解决方案。
答案 2 :(得分:1)
可能你需要存储一个字段&#34;版本&#34;与你的对象。
序列化对象时:将此字段设置为特定值,例如&#34; 1&#34;对于第一个版本。
如果更改序列化格式,请增加版本号。
反序列化对象时,请阅读版本号,并使用此版本的预期格式进行反序列化。如果您从保存的版本添加了字段,请正确处理此字段。如果您删除了某个字段,请将其忽略。
您需要保持源代码中的功能,以便从所有先前版本中反序列化对象。
您还可以使用某些库来执行此操作,例如protobuf或gson,这使您能够描述由某些字段组成的格式,具有特定版本号并自动处理预期字段。
答案 3 :(得分:0)
您可以尝试使用XML,JDOM为您提供了将JAVA解析为XML和XML到JAVA的简单工具