这个问题一直是我团队中一些热烈讨论的主题。我个人的选择是使用
@SuppressWarnings("serial")
我的想法是,这意味着与使用
相比,维护的东西要少一些serialVersionUID
我认为使用它可以让编译器生成UID,因此更有可能获得对类的更改吗?
我最担心的是,当他们改变课程时依赖开发人员更改UID更有可能导致无法预料的错误。
我的方法有什么陷阱吗?有没有其他人使用这些方法中的任何一个有好的或坏的经历?
答案 0 :(得分:6)
归结为这个问题:
“不同的代码”可能意味着几件事:
在这些情况下,您应该强烈遵守序列化合同 - 而这不仅仅通过设置serialVersionUId
来完成 - 通常您还必须覆盖序列化和反序列化的方法以应对不同的版本。
如果 - 另一方面 - 同一个程序读取和写入内容缓存之类的东西,并且该缓存可以在软件更新时从头开始重建 - 然后随意让你的生活像你一样简单可以。
在这些极端之间当然有各种各样的灰色阴影。
答案 1 :(得分:5)
像往常一样,明确的答案在其Effective Java的章节中。
TL; DR。使用@SuppressWarnings("serial")
很好(并且很适合删除警告),除非您确实要序列化该类。在这种情况下,您希望实际实现serialVersionUID
并正确执行,在类中的每次更改之后重新生成(或至少更改)它,这将导致反序列化中出现问题。
答案 2 :(得分:3)
序列化一个Object时,如果它有一个序列标识符,它将被使用,否则将被生成。
如果缺少序列标识符并且指定了javac
选项-Xlint
,则会生成警告。可以使用@SuppressWarnings("serial")
静音此警告。这个注释的目的只是为了抑制警告,它不会以任何其他方式干扰序列化。
来自Serializable Javadoc:
序列化运行时与每个可序列化类关联一个版本号,称为serialVersionUID,在反序列化期间用于验证序列化对象的发送方和接收方是否已加载该对象的类。兼容序列化。
还有:
如果可序列化类未显式声明serialVersionUID,则序列化运行时将根据类的各个方面为该类计算默认的serialVersionUID值,如Java(TM)中所述对象序列化规范。但是,强烈建议所有可序列化类显式声明serialVersionUID值,因为默认的serialVersionUID计算对类细节高度敏感,这些细节可能因编译器实现而异,因此在反序列化期间可能导致意外的InvalidClassExceptions 。
示例:使用@SuppressWarnings("serial")
Test.java:要序列化的类
import java.io.Serializable;
@SuppressWarnings("serial")
class Test implements Serializable {
int a = 0;
public Test() {
}
public Test(int arg) {
a = arg;
}
}
Writer.java:将Test实例写入test.serial文件
的类import java.io.*;
class Writer {
public static void main(String[] args) {
Test t = new Test(2);
String filename = "test.serial";
try {
FileOutputStream fos = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(t);
out.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
答案 3 :(得分:2)
不使用serialVersionUID
是保守的做法。序列化格式的任何更改都将被视为不兼容的更改。这意味着在反序列化旧版本编写的对象时可能会发现错误,可能实际上有意义反序列化,编写自定义反序列化代码并使用serialVersionUID
。
使用它的好处是可能保留了更多版本的序列化对象的兼容性,但是,引入了一种新的潜在错误:如果在以不兼容的方式更改类后忘记更新serialVersionUID
, 最好可能发生的事情是您在运行时也会收到错误,因为无法合理地读取数据。但是,它可能默默地成功,可能忽略或误解了过程中的序列化数据。
那么,首先要决定你想要哪一个:使用与否?我绝对默认不使用serialVersionUID
,除非明确需要它,并且承诺保持正确。
如果您不使用它,可以使用@SuppressWarnings
关闭警告,但是,您也可以使用javac
在-Xlint:-serial
全局关闭警告。这是一种粗略的方法 - 也许方便,但要确保你真的想要这样做。例如,如果您绝对不在任何地方使用serialVersionUID
,这是合适的。
答案 4 :(得分:1)
如果您认为将来需要向此类添加任何内容,那么如果您还没有序列化对象,请继续将serialVersionUID硬编码为1L。如果您已经有序列化对象并且需要与它们保持兼容,请使用serialver工具找出Java为此设置的内容并使用该值。
通过这种方式,您将来可以为此类添加属性,并且能够自动保持兼容。如果你不这样做,那么这将不会自动处理,你必须编写代码,以便在每次向类添加字段时将旧类转换为新类。
答案 5 :(得分:0)
我不得不说你选择SuppressWarning很糟糕。您应该明确定义serialVersionUID。保持它并不是那么多工作,而它将来会挽救你的生命。
将来的某个时候,您希望更改类定义(如果您的工作很严重,则不可避免),同时实现向后兼容性,serialVersionUID将挽救您的生命。如果你没有定义它,Java会根据类签名(?)为你计算serialVersionUID;简单地重新排序变量会改变签名并破坏兼容性(甚至添加辅助函数)。说真的,定义serialVersionUID会挽救你的生命。
您应该按照其他人的建议阅读Effective Java中的Serialization部分。