什么是serialVersionUID?为什么要使用它?
当 serialVersionUID
丢失时,Eclipse 会发出警告。
可序列化类 Foo 未声明静态final long 类型的serialVersionUID 字段
什么是 serialVersionUID
以及为什么它很重要? 请举例说明缺少 serialVersionUID
会导致问题的情况。
Eclipse issues warnings when a serialVersionUID
is missing.
The serializable class Foo does not declare a static final
serialVersionUID field of type long
What is serialVersionUID
and why is it important? Please show an example where missing serialVersionUID
will cause a problem.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(26)
首先我需要解释一下什么是序列化。
序列化允许将对象转换为流,以便通过网络发送该对象或保存到文件或保存到数据库以供以后使用。
序列化有一些规则。
仅当对象的类或其超类实现 Serialized 接口时,对象才是可序列化的
对象是可序列化的(其本身实现了 Serialized 接口),即使其超类不是可序列化的。 但是,可序列化类层次结构中的第一个超类(未实现 Serialized 接口)必须具有无参数构造函数。 如果违反了这一点,readObject()将在运行时产生java.io.InvalidClassException
所有基本类型都是可序列化的。
瞬态字段(带有瞬态修饰符)不会被序列化(即不保存或恢复)。 实现 Serialized 的类必须标记不支持序列化的类(例如文件流)的瞬态字段。
静态字段(带有 static 修饰符)未序列化。
当
Object
被序列化时,Java 运行时会关联序列版本号,即serialVersionID
。我们需要serialVersionID的地方:
在反序列化期间验证发送方和接收方在序列化方面是否兼容。 如果接收方使用不同的
serialVersionID
加载类,则反序列化将以InvalidClassCastException
结束。可序列化类可以通过声明名为
serialVersionUID
的字段来显式声明自己的serialVersionUID
,该字段必须是 static、final 且类型为 long。让我们通过一个例子来尝试一下。
创建序列化对象
反序列化对象
注意:现在更改 Employee 类的serialVersionUID 并保存:
并执行 Reader 类。 不执行 Writer 类,您将得到异常。
First I need to explain what serialization is.
Serialization allows to convert an object to a stream, for sending that object over the network OR Save to file OR save into DB for later usage.
There are some rules for serialization.
An object is serializable only if its class or its superclass implements the Serializable interface
An object is serializable (itself implements the Serializable interface) even if its superclass is not. However, the first superclass in the hierarchy of the serializable class, that does not implements Serializable interface, MUST have a no-arg constructor. If this is violated, readObject() will produce a java.io.InvalidClassException in runtime
All primitive types are serializable.
Transient fields (with transient modifier) are NOT serialized, (i.e., not saved or restored). A class that implements Serializable must mark transient fields of classes that do not support serialization (e.g., a file stream).
Static fields (with static modifier) are not serialized.
When
Object
is serialized, Java Runtime associates the serial version number aka, theserialVersionID
.Where we need serialVersionID:
During the deserialization to verify that sender and receiver are compatible with respect to serialization. If the receiver loaded the class with a different
serialVersionID
then deserialization will end withInvalidClassCastException
.A serializable class can declare its own
serialVersionUID
explicitly by declaring a field namedserialVersionUID
that must be static, final, and of type long.Let's try this with an example.
Create Serialize Object
Deserialize the object
NOTE: Now change the serialVersionUID of the Employee class and save:
And execute the Reader class. Not to execute the Writer class and you will get the exception.
如果您永远不需要将对象序列化为字节数组并发送/存储它们,那么您无需担心它。 如果这样做,那么您必须考虑您的serialVersionUID,因为对象的反序列化器会将其与其类加载器所具有的对象版本相匹配。 在 Java 语言规范中阅读更多相关信息。
If you will never need to serialize your objects to byte array and send/store them, then you don't need to worry about it. If you do, then you must consider your serialVersionUID since the deserializer of the object will match it to the version of object its classloader has. Read more about it in the Java Language Specification.
如果您在一个从未考虑过序列化的类上收到此警告,并且您没有声明自己
实现了 Serialized
,那么通常是因为您继承自实现了 Serialized 的超类。 通常,委托给这样的对象而不是使用继承会更好。因此,
在相关方法中调用
myList.foo()
而不是this.foo()
(或super.foo()
>)。 (这并不适合所有情况,但仍然很常见。)我经常看到人们扩展 JFrame 等,而实际上他们只需要委托给它。 (这也有助于在 IDE 中自动完成,因为 JFrame 有数百个方法,当您想在类上调用自定义方法时,您不需要这些方法。)
警告(或serialVersionUID)不可避免的一种情况是当您从 AbstractAction 扩展时,通常在匿名类中,仅添加 actionPerformed 方法。 我认为在这种情况下不应该出现警告(因为您通常无法在类的不同版本中可靠地序列化和反序列化此类匿名类),但我不确定编译器如何识别这一点。
If you get this warning on a class you don't ever think about serializing, and that you didn't declare yourself
implements Serializable
, it is often because you inherited from a superclass, which implements Serializable. Often then it would be better to delegate to such a object instead of using inheritance.So, instead of
do
and in the relevant methods call
myList.foo()
instead ofthis.foo()
(orsuper.foo()
). (This does not fit in all cases, but still quite often.)I often see people extending JFrame or such, when they really only need to delegate to this. (This also helps for auto-completing in a IDE, since JFrame has hundreds of methods, which you don't need when you want to call your custom ones on your class.)
One case where the warning (or the serialVersionUID) is unavoidable is when you extend from AbstractAction, normally in a anonymous class, only adding the actionPerformed-method. I think there shouldn't be a warning in this case (since you normally can't reliable serialize and deserialize such anonymous classes anyway accross different versions of your class), but I'm not sure how the compiler could recognize this.
要理解字段serialVersionUID的重要性,应该了解序列化/反序列化的工作原理。
当序列化类对象被序列化时,Java 运行时将序列化版本号(称为serialVersionUID)与该序列化对象相关联。 当您反序列化此序列化对象时,Java 运行时将序列化对象的serialVersionUID 与类的serialVersionUID 进行匹配。 如果两者相等,则仅继续进行进一步的反序列化过程,否则抛出 InvalidClassException。
因此我们得出结论,为了使序列化/反序列化过程成功,序列化对象的serialVersionUID必须等于类的serialVersionUID。 如果程序员在程序中显式指定serialVersionUID值,则相同的值将与序列化对象和类关联,而不管序列化和反序列化平台如何(例如,序列化可以在像windows这样的平台上使用sun或MS JVM 和反序列化可能位于使用 Zing JVM 的不同平台 Linux 上。
但是,如果程序员没有指定serialVersionUID,那么在对任何对象进行序列化\反序列化时,Java运行时将使用自己的算法来计算它。 这个serialVersionUID计算算法因JRE而异。 也有可能对象序列化的环境使用一个 JRE(例如:SUN JVM),而发生反序列化的环境使用 Linux Jvm(zing)。 在这种情况下,与序列化对象关联的serialVersionUID将不同于在反序列化环境中计算的类的serialVersionUID。 反过来反序列化也不会成功。 因此,为了避免这种情况/问题,程序员必须始终指定可序列化类的serialVersionUID。
To understand the significance of field serialVersionUID, one should understand how Serialization/Deserialization works.
When a Serializable class object is serialized Java Runtime associates a serial version no.(called as serialVersionUID) with this serialized object. At the time when you deserialize this serialized object Java Runtime matches the serialVersionUID of serialized object with the serialVersionUID of the class. If both are equal then only it proceeds with the further process of deserialization else throws InvalidClassException.
So we conclude that to make Serialization/Deserialization process successful the serialVersionUID of serialized object must be equivalent to the serialVersionUID of the class. In case if programmer specifies the serialVersionUID value explicitly in the program then the same value will be associated with the serialized object and the class, irrespective of the serialization and deserialzation platform(for ex. serialization might be done on platform like windows by using sun or MS JVM and Deserialization might be on different platform Linux using Zing JVM).
But in case if serialVersionUID is not specified by programmer then while doing Serialization\DeSerialization of any object, Java runtime uses its own algorithm to calculate it. This serialVersionUID calculation algorithm varies from one JRE to another. It is also possible that the environment where the object is serialized is using one JRE (ex: SUN JVM) and the environment where deserialzation happens is using Linux Jvm(zing). In such cases serialVersionUID associated with serialized object will be different than the serialVersionUID of class calculated at deserialzation environment. In turn deserialization will not be successful. So to avoid such situations/issues programmer must always specify serialVersionUID of Serializable class.
至于缺少serialVersionUID可能会导致问题的示例:
我正在开发这个Java EE应用程序,该应用程序由使用
EJB
模块的Web模块组成。 Web 模块远程调用EJB
模块,并传递实现Serialized
的POJO
作为参数。这个 POJO 类被打包在 EJB jar 中以及 Web 模块的 WEB-INF/lib 中它自己的 jar 中。 它们实际上是同一个类,但是当我打包 EJB 模块时,我解压了这个 POJO 的 jar,将其与 EJB 模块打包在一起。
对
EJB
的调用失败并出现以下异常,因为我没有声明其serialVersionUID
:As for an example where the missing serialVersionUID might cause a problem:
I'm working on this Java EE application that is composed of a Web module that uses an
EJB
module. The web module calls theEJB
module remotely and passes aPOJO
that implementsSerializable
as an argument.This
POJO's
class was packaged inside the EJB jar and inside it's own jar in the WEB-INF/lib of the web module. They're actually the same class, but when I package the EJB module I unpack this POJO's jar to pack it together with the EJB module.The call to the
EJB
was failing with the Exception below because I hadn't declared itsserialVersionUID
:不用担心,默认计算确实很好,足以满足 99,9999% 的情况。 如果您遇到问题,您可以 - 如前所述 - 在需要时引入 UID(这是极不可能的)
Don't bother, the default calculation is really good and suffice for 99,9999% of the cases. And if you run into problems, you can - as already stated - introduce UID's as the need arrise (which is highly unlikely)
我通常在一个上下文中使用
serialVersionUID
:当我知道它将离开 Java VM 的上下文时。当我为我的应用程序使用
ObjectInputStream
和ObjectOutputStream
时,或者如果我知道我使用的库/框架将使用它时,我就会知道这一点。 SerialVersionID 确保不同版本或供应商的不同 Java VM 能够正确互操作,或者如果在 VM 外部存储和检索它(例如HttpSession
),则即使在应用程序重新启动和升级期间,会话数据也可以保留服务器。对于所有其他情况,我都会使用,
因为大多数时候默认的
serialVersionUID
就足够了。 这包括Exception
、HttpServlet
。I generally use
serialVersionUID
in one context: When I know it will be leaving the context of the Java VM.I would know this when I to use
ObjectInputStream
andObjectOutputStream
for my application or if I know a library/framework I use will use it. The serialVersionID ensures different Java VMs of varying versions or vendors will inter-operate correctly or if it is stored and retrieved outside the VM for exampleHttpSession
the session data can remain even during a restart and upgrade of the application server.For all other cases, I use
since most of the time the default
serialVersionUID
is sufficient. This includesException
,HttpServlet
.字段数据代表类中存储的一些信息。
类实现了
Serialized
接口,因此 Eclipse 自动提供声明
serialVersionUID
字段。 让我们从设置的值 1 开始。如果您不希望出现该警告,请使用以下命令:
Field data represents some information stored in the class.
Class implements the
Serializable
interface,so eclipse automatically offered to declare the
serialVersionUID
field. Lets start with value 1 set there.If you don't want that warning to come, use this:
为什么在 Java 中的
Serialized
类中使用SerialVersionUID
?在
序列化
期间,Java 运行时为类创建一个版本号,以便稍后可以对其进行反序列化。 此版本号在 Java 中称为SerialVersionUID
。SerialVersionUID
用于对序列化数据进行版本控制。 仅当类的SerialVersionUID
与序列化实例匹配时,您才能反序列化该类。 当我们不在类中声明SerialVersionUID
时,Java 运行时会为我们生成它,但不建议这样做。 建议将SerialVersionUID
声明为private static final long
变量以避免默认机制。当您通过实现标记接口 java.io.Serialized 将类声明为可序列化时,Java 运行时将使用默认的序列化机制将该类的实例保留到磁盘中,前提是您没有这样做使用
Externalized
接口自定义流程。另请参见 为什么在内部使用 SerialVersionUID Java中的可序列化类
Why use
SerialVersionUID
insideSerializable
class in Java?During
serialization
, Java runtime creates a version number for a class, so that it can de-serialize it later. This version number is known asSerialVersionUID
in Java.SerialVersionUID
is used to version serialized data. You can only de-serialize a class if it'sSerialVersionUID
matches with the serialized instance. When we don't declareSerialVersionUID
in our class, Java runtime generates it for us but its not recommended. It's recommended to declareSerialVersionUID
asprivate static final long
variable to avoid default mechanism.When you declare a class as
Serializable
by implementing marker interfacejava.io.Serializable
, Java runtime persist instance of that class into disk by using default Serialization mechanism, provided you have not customized the process usingExternalizable
interface.see also Why use SerialVersionUID inside Serializable class in Java
SerialVersionUID用于对象的版本控制。 您也可以在类文件中指定serialVersionUID。 不指定serialVersionUID的后果是,当您添加或修改类中的任何字段时,已序列化的类将无法恢复,因为为新类和旧序列化对象生成的serialVersionUID将不同。 Java 序列化过程依赖于正确的serialVersionUID 来恢复序列化对象的状态,并在serialVersionUID 不匹配的情况下抛出java.io.InvalidClassException
阅读更多:http://javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html#ixzz3VQxnpOPZ
SerialVersionUID is used for version control of object. you can specify serialVersionUID in your class file also. Consequence of not specifying serialVersionUID is that when you add or modify any field in class then already serialized class will not be able to recover because serialVersionUID generated for new class and for old serialized object will be different. Java serialization process relies on correct serialVersionUID for recovering state of serialized object and throws java.io.InvalidClassException in case of serialVersionUID mismatch
Read more: http://javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html#ixzz3VQxnpOPZ
如果 CheckStyle 能够验证实现 Serialized 的类上的serialVersionUID 是否具有良好的值,即它与串行版本 id 生成器将生成的内容相匹配,那就太好了。 例如,如果您有一个包含大量可序列化 DTO 的项目,那么记住删除现有的 serialVersionUID 并重新生成它是一件痛苦的事情,目前(据我所知)验证这一点的唯一方法是为每个类重新生成并与旧的。 这是非常非常痛苦的。
It would be nice if CheckStyle could verify that the serialVersionUID on a class that implements Serializable has a good value, i.e. that it matches what the serial version id generator would produce. If you have a project with lots of serializable DTOs, for example, remembering to delete the existing serialVersionUID and regenerate it is a pain, and currently the only way (that I know of) to verify this is to regenerate for each class and compare to the old one. This is very very painful.
如果你想修改大量没有设置serialVersionUID的类,同时保持与旧类的兼容性,像IntelliJ Idea、Eclipse这样的工具就不够了,因为它们会生成随机数,并且不能处理一堆文件一气呵成。 我提出了以下 bash 脚本(对于 Windows 用户,我很抱歉,请考虑购买 Mac 或转换为 Linux)来轻松修改 serialVersionUID 问题:
保存此脚本,将 add_serialVersionUID.sh 保存到 ~/bin 中。 然后,您在 Maven 或 Gradle 项目的根目录中运行它,如下所示:
此 .lst 包含用于添加serialVersionUID 的 java 文件列表,格式如下:
此脚本在后台使用 JDK serialVer 工具。 因此,请确保您的 $JAVA_HOME/bin 在 PATH 中。
If you want to amend a huge number of classes which had no serialVersionUID set in the first place while maintain the compatibility with the old classes, tools like IntelliJ Idea, Eclipse fall short as they generate random numbers and does not work on a bunch of files in one go. I come up the following bash script(I'm sorry for Windows users, consider buy a Mac or convert to Linux) to make amending serialVersionUID issue with ease:
you save the this script, say add_serialVersionUID.sh to you ~/bin. Then you run it in the root directory of your Maven or Gradle project like:
This .lst includes the list of java files to add the serialVersionUID in the following format:
This script uses the JDK serialVer tool under hood. So make sure your $JAVA_HOME/bin is in the PATH.
Joshua Bloch 在《Effective Java》中详细记录了这个问题。 一本非常好的书,必读。 我将概述以下一些原因:
序列化运行时为每个可序列化类提供一个称为“序列版本”的数字。 这个数字称为serialVersionUID。 现在这个数字背后有一些数学,它是根据类中定义的字段/方法得出的。 对于同一个类,每次都会生成相同的版本。 在反序列化期间使用此数字来验证序列化对象的发送者和接收者是否已为该对象加载了与序列化兼容的类。 如果接收方加载的对象类与相应发送方的类具有不同的serialVersionUID,则反序列化将导致 InvalidClassException。
如果类是可序列化的,您还可以通过声明名为“serialVersionUID”的字段来显式声明您自己的serialVersionUID,该字段必须是静态的、最终的且类型为long。 大多数 IDE(例如 Eclipse)可以帮助您生成那么长的字符串。
This question is very well documented in Effective Java by Joshua Bloch. A very good book and a must read. I will outline some of the reasons below :
The serialization runtime comes up with a number called Serial version for each serializable class. This number is called serialVersionUID. Now there is some Math behind this number and it comes out based on the fields/methods that are defined in the class. For the same class the same version is generated every time. This number is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization. If the receiver has loaded a class for the object that has a different serialVersionUID than that of the corresponding sender's class, then deserialization will result in an InvalidClassException.
If the class is serializable you can also declare your own serialVersionUID explicitly by declaring a field named "serialVersionUID" that must be static, final, and of type long. Most IDE's like Eclipse help you generate that long string.
每次序列化对象时,都会为该对象的类打上版本 ID 号的标记。此 ID 称为 serialVersionUID 且它是根据有关类结构的信息计算的。 假设你创建了一个 Employee 类,它的版本 ID #333(由 JVM 分配),现在当你序列化该类的对象(假设 Employee 对象)时,JVM 会将 UID 分配给它#333。
考虑一种情况 - 将来您需要编辑或更改您的类,在这种情况下,当您修改它时,JVM 将为它分配一个新的 UID(假设#444)。
现在,当您尝试反序列化员工对象时,JVM 会将序列化对象(员工对象)的版本 ID(#333)与类的版本 ID(#444)进行比较(因为它已更改)。 比较后,JVM 会发现两个版本的 UID 不同,因此反序列化将失败。
因此,如果每个类的serialVersionID是由程序员自己定义的。 即使类将来进化,也会是一样的,因此即使类发生了变化,JVM 总会发现该类与序列化对象兼容。 更多信息可以参考 HEAD FIRST JAVA 第 14 章。
Each time an object is serialized the object is stamped with a version ID number for the object's class.This ID is called serialVersionUID and it is computed based on information about the class structure. Suppose you made an Employee class and it has version id #333 (assigned by JVM),Now when you will serialize the object of that class (Suppose Employee object), JVM will assign UID to it as #333.
Consider a situation - in the future you need to edit or change your class and in that case when you modify it, JVM will assign it a new UID (Suppose #444).
Now when you try to deserialize the employee object, JVM will compare serialized object's (Employee object) version ID(#333) with that of the class i.e #444(Since it was changed). On comparison JVM will find both version UID are different and hence Deserialization will fail.
Hence if serialVersionID for each class is defined by programmer itself. It will be same even if the class is evolved in future and hence JVM will always find that class is compatible with serialized object even though the class is changed. For more Info you can refer chapter 14 of HEAD FIRST JAVA.
一个简单的解释:
你正在序列化数据吗?
序列化基本上是将类数据写入文件/流/等。 反序列化是将数据读回类。
您打算投入生产吗?
如果您只是使用不重要/虚假数据测试某些内容,那么不必担心(除非您直接测试序列化)。
这是第一个版本吗?
如果是这样,请设置
serialVersionUID=1L
。这是第二个、第三个等产品版本吗?
现在您需要担心
serialVersionUID
,并且应该深入研究它。基本上,如果在更新需要写入/读取的类时没有正确更新版本,那么当您尝试读取旧数据时将会收到错误。
A Simple Explanation:
Are you serializing data?
Serialization is basically writing class data to a file/stream/etc. De-serialization is reading that data back to a class.
Do you intend to go into production?
If you are just testing something with unimportant/fake data, then don't worry about it (unless you are testing serialization directly).
Is this the first version?
If so, set
serialVersionUID=1L
.Is this the second, third, etc. prod version?
Now you need to worry about
serialVersionUID
, and should look into it in depth.Basically, if you don't update the version correctly when you update a class you need to write/read, you will get an error when you try to read old data.
长话短说,该字段用于检查序列化数据是否可以正确反序列化。 序列化和反序列化通常由程序的不同副本进行 - 例如服务器将对象转换为字符串,客户端将接收到的字符串转换为对象。 该字段表明两者对于该对象是什么有相同的想法。 此字段在以下情况下会有所帮助:
您在不同位置有许多不同的程序副本(例如 1 个服务器和 100 个客户端)。 如果你要改变你的对象,改变你的版本号,并且忘记更新这个客户端,它就会知道他不能反序列化
您已将数据存储在某个文件中,稍后您尝试使用更新的文件打开它带有修改对象的程序版本 - 如果您保持版本正确,您就会知道该文件不兼容
什么时候它很重要?
最明显的是 - 如果您向对象添加一些字段,旧版本将无法使用它们,因为它们的对象结构中没有这些字段。
不太明显 - 当您反序列化对象时,字符串中不存在的字段将保留为 NULL。 如果您从对象中删除了字段,旧版本会将此字段保留为 allways-NULL,如果旧版本依赖于此字段中的数据,则可能会导致错误行为(无论如何,您创建它是为了某些东西,而不仅仅是为了好玩:-))
最不明显 - 有时您会改变在某些字段的含义中所表达的想法。 例如,当您 12 岁时,您的意思是“自行车”下的“自行车”,但当您 18 岁时,您的意思是“摩托车” - 如果您的朋友邀请您“骑自行车穿越城市”,那么您将是唯一一个骑着自行车来,你就会明白在不同领域保持相同的含义是多么重要:-)
To tell the long story short this field is used to check if serialized data can be deserialized correctly. Serialization and deserialization are often made by different copies of program - for example server converts object to string and client converts received string to object. This field tells that both operates with same idea about what this object is. This field helps when:
you have many different copies of your program in different places (like 1 server and 100 clients). If you will change your object, alter your version number and forget to update one this clients, it will know that he is not capable of deserialization
you have stored your data in some file and later on you try to open it with updated version of your program with modified object - you will know that this file is not compatible if you keep your version right
When is it important?
Most obvious - if you add some fields to your object, older versions will not be able to use them because they do not have these fields in their object structure.
Less obvious - When you deserialize object, fields that where not present in string will be kept as NULL. If you have removed field from your object, older versions will keep this field as allways-NULL that can lead to misbehavior if older versions rely on data in this field (anyway you have created it for something, not just for fun :-) )
Least obvious - Sometimes you change the idea you put in some field's meaning. For example when you are 12 years old you mean "bicycle" under "bike", but when you are 18 you mean "motorcycle" - if your friends will invite you to "bike ride across city" and you will be the only one who came on bicycle, you will undestand how important it is to keep same meaning across fields :-)
“serialVersionUID”是一个 64 位数字,用于在反序列化过程中唯一标识一个类。 当序列化一个对象时,该类的serialVersionUID也会写入该文件。 每当反序列化该对象时,java运行时都会从序列化数据中提取该serialVersionUID值,并将与该类关联的相同值进行比较。 如果两者不匹配,则会抛出“java.io.InvalidClassException”。
如果一个可序列化的类没有显式声明一个serialVersionUID,那么序列化运行时将根据该类的各个方面(如字段、方法等)计算该类的serialVersionUID值,您可以参考这个演示应用程序的链接。
'serialVersionUID' is a 64 bit number used to uniquely identify a class during deserialization process. When you serialize an object, serialVersionUID of the class also written to the file. Whenever you deserialize this object, java run time extract this serialVersionUID value from the serialized data and compare the same value associate with the class. If both do not match, then 'java.io.InvalidClassException' will be thrown.
If a serializable class do not explicitly declare a serialVersionUID, then serialization runtime will calculate serialVersionUID value for that class based on various aspects of the class like fields, methods etc.,, You can refer this link for demo application.
首先回答你的问题,当我们不在类中声明 SerialVersionUID 时,Java 运行时会为我们生成它,但该过程对许多类元数据敏感,包括字段数量、字段类型、字段访问修饰符、实现的接口因此,建议我们自己声明它,并且 Eclipse 会警告您同样的情况。
序列化:
我们经常使用重要的对象,其状态(对象变量中的数据)非常重要,以至于在将对象状态发送到其他机器时,我们不能冒因电源/系统故障(或)网络故障而丢失它的风险。 这个问题的解决方案被称为“持久化”,它的意思就是持久化(保存/保存)数据。 序列化是实现持久性的许多其他方法之一(通过将数据保存到磁盘/内存)。 保存对象的状态时,为对象创建一个标识非常重要,以便能够正确地读回它(反序列化)。 这个唯一标识ID就是SerialVersionUID。
Firstly to answer your question, when we don't declare SerialVersionUID in our class, Java runtime generates it for us, but that process is sensitive to many class meta data including number of fields, type of fields, access modifier of fields, interface implemented by class etc. Therefore it is recommended to declare it ourselves and Eclipse is warning you about the same.
Serialization:
We often work with important objects whose state (data in the variables of the object) is so important that we can not risk to lose it due to power/system failures (or) network failures in case of sending the object state to other machine. The solution for this problem is named "Persistence" which simply means persisting (holding/saving) the data. Serialization is one of many other ways to achieve persistence (by saving data to disk/memory). When saving the state of the object, it is important to create an identity for the object, to be able to properly read it back (de-serialization). This unique identification is ID is SerialVersionUID.
您不应该不手动定义serialVersionUID,至少在没有仔细考虑的情况下是这样。
许多教程、开发人员和 linting 工具对这个领域都有错误的想法。 lombok-team 的这篇优秀文章解释了为什么工具不能生成serialVersionUID,并且在这个过程中也解释了许多开发人员对这个概念的误解,我强烈建议在尝试任何“解决方案”之前阅读整篇文章这个问题。
https://github.com/projectlombok/lombok/wiki/WHY-NOT :-serialVersionUID
它们解释了 Java 中的serialVersionUID 机制的作用:
不定义serialVersionUID所获得的默认行为实际上正是您想要的。如果可序列化类发生更改,系统会阻止您将旧数据反序列化到其中,因为这可能会产生垃圾。
仅当您想要将旧数据反序列化到修改后的类中时,才需要通过在类上定义一个serialVersionUID来告诉Java这是可以的,并确保您实际上可以读取旧数据嗯>。 您可以通过不进行损害二进制兼容性的更改或使用 writeReplace/readResolve 或 writeObject/readObject 来实现此目的。
这很困难,并且正确地实现这一点的责任在于程序员。 你真的应该考虑一下是否有必要。
You should not manually define serialVersionUID, at least not without careful consideration.
Many tutorials, developers and linting-tools have wrong ideas about this field. This excellent article from the lombok-team exists to explain why serialVersionUID cannot be generated by a tool and in that process also explains what many developers get wrong about this concept, I strongly recommend reading the whole thing before trying any of the 'solutions' to this question.
https://github.com/projectlombok/lombok/wiki/WHY-NOT:-serialVersionUID
They explain what the serialVersionUID-mechanism does in Java:
The default-behaviour you get from not defining serialVersionUID is actually what you want. If a serializable class changes, the system prevents you from deserializing old data into it, as it would potentially produce rubbish.
Only if you want to deserialize old data into a modified class it becomes necessary to tell Java this is OK by defining a serialVersionUID on your class and make sure that you actually can read old data. You can achieve this by never doing a change that hurts binary compatibility or by using writeReplace/readResolve, or writeObject/readObject.
This is hard, and the responsibility for getting this correct is on the programmer. You should really think about whether that is necessary.
java.io.Serialized
可能是您得到的最好的解释:The docs for
java.io.Serializable
are probably about as good an explanation as you'll get:如果您序列化只是因为您必须为了实现而序列化(谁在乎您是否为
HTTPSession
序列化,例如......如果它已存储或未存储,您可能不关心反序列化
表单对象),那么你可以忽略它。如果您实际上正在使用序列化,那么只有您计划直接使用序列化来存储和检索对象才重要。
serialVersionUID
代表您的类版本,如果类的当前版本与其先前版本不向后兼容,则应该增加它。大多数时候,您可能不会直接使用序列化。 如果是这种情况,请通过单击快速修复选项生成默认的
SerialVersionUID
,不必担心。If you're serializing just because you have to serialize for the implementation's sake (who cares if you serialize for an
HTTPSession
, for instance...if it's stored or not, you probably don't care aboutde-serializing
a form object), then you can ignore this.If you're actually using serialization, it only matters if you plan on storing and retrieving objects using serialization directly. The
serialVersionUID
represents your class version, and you should increment it if the current version of your class is not backwards compatible with its previous version.Most of the time, you will probably not use serialization directly. If this is the case, generate a default
SerialVersionUID
by clicking the quick fix option and don't worry about it.我不能错过这个机会来插入 Josh Bloch 的书 Effective Java(第 2 期)版)。 第10章是Java序列化的必备资源。
根据 Josh 的说法,自动生成的 UID 是根据类名、实现的接口以及所有公共和受保护成员生成的。 以任何方式更改其中任何一个都会更改
serialVersionUID
。 因此,只有当您确定不会序列化该类的多个版本(无论是跨进程还是稍后从存储中检索)时,您才不需要弄乱它们。如果你暂时忽略它们,后来发现你需要以某种方式更改类但保持与旧版本类的兼容性,你可以使用 JDK 工具 serialver 生成
serialVersionUID
在旧类上,并在新类上显式设置它。 (根据您的更改,您可能还需要通过添加 writeObject 和 readObject 方法来实现自定义序列化 - 请参阅 javadoc 或上述第 10 章。)I can't pass up this opportunity to plug Josh Bloch's book Effective Java (2nd Edition). Chapter 10 is an indispensible resource on Java serialization.
Per Josh, the automatically-generated UID is generated based on a class name, implemented interfaces, and all public and protected members. Changing any of these in any way will change the
serialVersionUID
. So you don't need to mess with them only if you are certain that no more than one version of the class will ever be serialized (either across processes or retrieved from storage at a later time).If you ignore them for now, and find later that you need to change the class in some way but maintain compatibility w/ old version of the class, you can use the JDK tool serialver to generate the
serialVersionUID
on the old class, and explicitly set that on the new class. (Depending on your changes you may need to also implement custom serialization by addingwriteObject
andreadObject
methods - seeSerializable
javadoc or aforementioned chapter 10.)您可以告诉 Eclipse 忽略这些serialVersionUID 警告:
如果您不知道,您可以在本节中启用许多其他警告(甚至将一些报告为错误),其中许多都非常有用:
等等。
You can tell Eclipse to ignore these serialVersionUID warnings:
In case you didn't know, there are a lot of other warnings you can enable in this section (or even have some reported as errors), many are very useful:
and many more.
serialVersionUID
有助于序列化数据的版本控制。 序列化时其值与数据一起存储。 反序列化时,会检查相同版本,看看序列化数据与当前代码的匹配情况。如果您想对数据进行版本控制,通常从
serialVersionUID
0 开始,并在类的每次结构更改中更改序列化数据(添加或删除非瞬态字段)。内置的反序列化机制 (
in.defaultReadObject()
) 将拒绝对旧版本的数据进行反序列化。 但如果您愿意,您可以定义自己的 readObject() - 可以读回旧数据的函数。 然后,此自定义代码可以检查serialVersionUID
以了解数据所在的版本并决定如何反序列化它。 如果您存储的序列化数据在代码的多个版本中仍然存在,则此版本控制技术非常有用。但存储如此长的时间跨度的序列化数据并不常见。 更常见的是使用序列化机制将数据临时写入缓存等,或通过网络将其发送到具有相同版本的代码库相关部分的另一个程序。
在这种情况下,您对维护向后兼容性不感兴趣。 您只关心确保正在通信的代码库确实具有相同版本的相关类。 为了便于进行此类检查,您必须像以前一样维护
serialVersionUID
,并且在更改类时不要忘记更新它。如果您确实忘记更新该字段,则可能会得到一个类的两个不同版本,它们具有不同的结构,但具有相同的
serialVersionUID
。 如果发生这种情况,默认机制 (in.defaultReadObject()
) 将不会检测到任何差异,并尝试反序列化不兼容的数据。 现在,您可能会遇到神秘的运行时错误或静默失败(空字段)。 这些类型的错误可能很难发现。因此,为了帮助此用例,Java 平台为您提供了不手动设置
serialVersionUID
的选择。 相反,类结构的哈希值将在编译时生成并用作 id。 这种机制将确保您永远不会拥有具有相同 id 的不同类结构,因此您不会遇到上面提到的这些难以跟踪的运行时序列化失败。但自动生成的 ID 策略有一个缺点。 也就是说,不同编译器为同一类生成的 id 可能不同(如上面 Jon Skeet 提到的)。 因此,如果您在使用不同编译器编译的代码之间传递序列化数据,建议无论如何手动维护 ids。
如果您像前面提到的第一个用例那样向后兼容数据,您可能还想自己维护 id。 这是为了获得可读的 ID 并更好地控制它们何时以及如何更改。
serialVersionUID
facilitates versioning of serialized data. Its value is stored with the data when serializing. When de-serializing, the same version is checked to see how the serialized data matches the current code.If you want to version your data, you normally start with a
serialVersionUID
of 0, and bump it with every structural change to your class which alters the serialized data (adding or removing non-transient fields).The built-in de-serialization mechanism (
in.defaultReadObject()
) will refuse to de-serialize from old versions of the data. But if you want to you can define your own readObject()-function which can read back old data. This custom code can then check theserialVersionUID
in order to know which version the data is in and decide how to de-serialize it. This versioning technique is useful if you store serialized data which survives several versions of your code.But storing serialized data for such a long time span is not very common. It is far more common to use the serialization mechanism to temporarily write data to for instance a cache or send it over the network to another program with the same version of the relevant parts of the codebase.
In this case you are not interested in maintaining backwards compatibility. You are only concerned with making sure that the code bases which are communicating indeed have the same versions of relevant classes. In order to facilitate such a check, you must maintain the
serialVersionUID
just like before and not forget to update it when making changes to your classes.If you do forget to update the field, you might end up with two different versions of a class with different structure but with the same
serialVersionUID
. If this happens, the default mechanism (in.defaultReadObject()
) will not detect any difference, and try to de-serialize incompatible data. Now you might end up with a cryptic runtime error or silent failure (null fields). These types of errors might be hard to find.So to help this usecase, the Java platform offers you a choice of not setting the
serialVersionUID
manually. Instead, a hash of the class structure will be generated at compile-time and used as id. This mechanism will make sure that you never have different class structures with the same id, and so you will not get these hard-to-trace runtime serialization failures mentioned above.But there is a backside to the auto-generated id strategy. Namely that the generated ids for the same class might differ between compilers (as mentioned by Jon Skeet above). So if you communicate serialized data between code compiled with different compilers, it is recommended to maintain the ids manually anyway.
And if you are backwards-compatible with your data like in the first use case mentioned, you also probably want to maintain the id yourself. This in order to get readable ids and have greater control over when and how they change.
SerialVersionUID
是每个类的唯一标识符,JVM
使用它来比较类的版本,确保在反序列化期间加载序列化期间使用的相同类。指定一个可以提供更多控制,尽管如果您不指定,JVM 也会生成一个。 不同编译器生成的值可能不同。 此外,有时您只是出于某种原因想要禁止旧序列化对象的反序列化[
向后不兼容
],在这种情况下,您只需更改serialVersionUID。Serialized 的 javadocs
说:因此,您必须声明serialVersionUID,因为它给我们更多的控制权。
本文对此主题有一些很好的观点。
SerialVersionUID
is a unique identifier for each class,JVM
uses it to compare the versions of the class ensuring that the same class was used during Serialization is loaded during Deserialization.Specifying one gives more control, though JVM does generate one if you don't specify. The value generated can differ between different compilers. Furthermore, sometimes you just want for some reason to forbid deserialization of old serialized objects [
backward incompatibility
], and in this case you just have to change the serialVersionUID.The javadocs for
Serializable
say:Therefore, you must declare serialVersionUID because it give us more control.
This article has some good points on the topic.
最初的问题询问了“为什么它很重要”和“示例”,其中此
序列版本 ID
会很有用。 好吧,我找到了一个。假设您创建一个
Car
类,实例化它,并将其写入对象流。 展平的汽车对象会在文件系统中保留一段时间。 同时,如果通过添加新字段来修改Car
类。 稍后,当您尝试读取(即反序列化)扁平化的Car
对象时,您会得到java.io.InvalidClassException
– 因为所有可序列化的类都会自动获得唯一的标识符。 当类的标识符不等于扁平化对象的标识符时,会抛出此异常。 如果你认真思考一下,抛出异常是因为添加了新字段。 您可以通过声明显式的serialVersionUID 自行控制版本控制来避免引发此异常。 显式声明您的serialVersionUID
也有一个小的性能优势(因为不需要计算)。 因此,最佳实践是在创建可序列化类后立即将您自己的serialVersionUID添加到它们中,如下所示:Original question has asked for 'why is it important' and 'example' where this
Serial Version ID
would be useful. Well I have found one.Say you create a
Car
class, instantiate it, and write it out to an object stream. The flattened car object sits in the file system for some time. Meanwhile, if theCar
class is modified by adding a new field. Later on, when you try to read (i.e. deserialize) the flattenedCar
object, you get thejava.io.InvalidClassException
– because all serializable classes are automatically given a unique identifier. This exception is thrown when the identifier of the class is not equal to the identifier of the flattened object. If you really think about it, the exception is thrown because of the addition of the new field. You can avoid this exception being thrown by controlling the versioning yourself by declaring an explicit serialVersionUID. There is also a small performance benefit in explicitly declaring yourserialVersionUID
(because does not have to be calculated). So, it is best practice to add your own serialVersionUID to your Serializable classes as soon as you create them as shown below: