有三种方法可以定义serialVersionUID:
1. private static final long serialVersionUID = 1L; (Default)
2. private static final long serialVersionUID = -8940196742313994740L; (Generated)
3. Don't define serialVersionUID and let the JVM define it at runtime. @Lance Java
但我不明白第一种方式!
我已经看过了,有人为源代码中的所有java类定义了“serialVersionUID = 1L”。
是什么意思?这有用吗?
如果所有类都具有相同的serialVersionUID 1L,那么没有问题吗?
答案 0 :(得分:12)
是什么意思?这有用吗?
是。 serialVersionUID的目的是让程序员控制哪个类的版本在序列化方面被认为是不兼容的。只要serialVersionUID保持不变,序列化机制将尽最大努力翻译序列化实例,这可能不是您想要的。如果进行语义更改以使旧版本不兼容,则可以更改serialVersionUID以使反序列化旧实例失败。
如果所有类都具有相同的serialVersionUID 1L,那么没有问题吗?
不 - serialVersionUID是每个。
答案 1 :(得分:9)
这是解释here:
serialVersionUID是Serializable类的通用版本标识符。反序列化使用此数字来确保加载的类与序列化对象完全对应。如果未找到匹配项,则抛出InvalidClassException。
来自javadoc:
序列化运行时将每个可序列化类与版本号相关联,称为serialVersionUID,在反序列化期间使用该版本号来验证序列化对象的发送方和接收方是否已加载与该序列化兼容的该对象的类。如果接收者为具有与相应发送者类的serialVersionUID不同的对象加载了一个类,则反序列化将导致InvalidClassException。可序列化类可以通过声明名为“serialVersionUID”的字段来明确声明其自己的serialVersionUID,该字段必须是static,final和long类型:
有用的链接
答案 2 :(得分:6)
如果序列化对象的InvalidClassException
与正在反序列化的类的serialVersionUID
不匹配,JVM将抛出serialVersionUID
。
每次类以不兼容的方式更改时,类的serialVersionUID
都应该更改。通常这意味着每次更改类的shape
(即字段或方法更改)。
在某些情况下,您不希望更改serialVersionUID
。例如,您可以在应用程序中接受旧版本的对象。在这种情况下,您可以保持serialVersionUID
相同,新字段将为空。
答案 3 :(得分:6)
是的,我已经看到了定义serialVersionUID
的代码。我认为这是一个坏主意。
在此上下文中,serialVersionUID
字段只有一个“不同”值;即零... 0L
。零表示“根据您正在序列化/反序列化的实际类”,通过应用标准算法“在运行时计算版本ID 。这意味着只要代码的有效序列化签名发生更改,序列化/反序列化代码就会使用不同的版本ID。从(大图)类型的安全角度来看,这是最安全的事情,尽管它也有些低效,而且可能更加脆弱。
是什么意思?
1L
没有特殊意义。它只是一个将1L
与“其他”版本ID匹配的数字。
在我看来,使用0L
或使用标准算法生成的(在某些时候)版本号最好。
如果您使用0L
,那么如果类以可能导致问题的方式发生变化,您将获得明确的反序列化异常。如果你需要这个,这是一件好事。
另一方面,您使用生成的版本ID,您(程序员)可以自行决定何时重新生成ID。当您决定重新生成时,只有在类签名已更改时,ID才会更改。 (如果类表示等没有改变,重新生成的签名应该与原始签名相同!)当id 更改时,你可以考虑是否添加自定义方法('readObject',等)处理不兼容问题。
但是,如果您使用1L
,则无法确定版本ID 是否需要更改而不检查您的代码历史记录,并比较旧版本/新版本课程......根据你的需要回来。
这有用吗?
这取决于您认为“有用”的含义。如果您认为将版本ID硬连接到“相信我,没关系”是件好事,那么1L
就很有用了。
我的回忆是,某些版本的Eclipse提供1L
作为缺少serialVersionUID
字段警告的可能自动更正之一。这可能是你看到的实例来自哪里。
答案 4 :(得分:5)
想象一下你用serialVersionUID
编写一个类,实例化它,然后将其序列化为一个文件(使用ObjectOutputStream)
然后修改课程。
然后,使用修改后的类,反序列化(读入)修改前序列化的版本。 Java将检查当前类和序列化类的serialVersionUID
,如果它们不匹配,则反序列化将失败。这是一个故意的快速失败,以防止由于类版本不兼容而在以后发生的更微妙(并且更难调试)错误。
如果省略serialVersionUID
,则禁用版本检查。
如果总是将if设置为1L,那么检查将始终通过(值始终相同),因此您仍然容易受到细微的类版本不兼容问题的影响。
答案 5 :(得分:3)
在序列化对象并将对象反序列化回JVM时使用该值。
此外,如果您的类发生更改并且您不希望支持向后兼容性(即能够反序列化使用上一版本类序列化的对象),则可以将版本号更改为任何其他值。
但是,要支持向后兼容性,您需要保留与之前设置的serialVersionUID值相同的版本号。
最佳做法是每次对类进行一些不兼容的更改时更改serialVersionUID。
答案 6 :(得分:2)
您可以将任何long
值分配给serialVersionUID
,但每次修改课程时都必须更改它。
第二个看起来像生成的serialVersionUID
,基于当前类版本的功能。
答案 7 :(得分:2)
重要的是要明确指出,让一个类实现Serializable
界面会使所有字段未被声明transient
导出的API 该类的,无论这些字段是否被声明为private
。
换句话说,实现Serializable:
打破封装。如果班级有机会成为一个成功的,长寿的班级,那么你必须支持序列化的表格......永远。
会严重影响您升级该类的能力,正是因为它是其导出的API的一部分。另一种方法是打破向后兼容性。
可能会为您的班级及其应用程序带来安全问题。反序列化代表了一种在没有构造函数的情况下创建Java对象的方法,因此可以通过向反序列化工具提供恶意字节流来违反类的不变量。
serialVerionUID
应被视为序列化表单的属性。它旨在向一个JVM传达它接收的类实例的序列化形式与在其他地方呈现的同一类的序列化形式之间是否存在差异。
如果序列化表单不同但UID相同,您可以看到可能出现的问题。接收JVM将假定在旧类和新类之间接收到的串行格式版本在它们不相同时将是相同的,并将尽职尽责并尝试反序列化字节流。
TLDR:您不应该在想要的时候更改UID。当类的序列化表单发生更改时,您应该更改它,以便使用您的类的旧版本(具有不同的序列化形式)的软件版本将会中断,而不是(可能是默默地)执行错误的操作。如果没有设计一个好的序列化表单,那么你的类将为其客户提供向后兼容性变得更加困难(甚至更难)。在理想的情况下,类的序列化形式在整个演变过程中都会持续存在(因此其UID不需要改变)。