Eclipse issues warnings when a serialVersionUID
is missing.
当一个serialVersionUID丢失时,Eclipse会发出警告。
The serializable class Foo does not declare a static final serialVersionUID field of type long
serializable类Foo没有声明一个类型为long的静态最终serialVersionUID字段
What is serialVersionUID
and why is it important? Please show an example where missing serialVersionUID
will cause a problem.
什么是serialVersionUID,为什么它很重要?请显示缺少serialVersionUID将导致问题的示例。
21 个解决方案
#1
1875
The docs for java.io.Serializable
are probably about as good an explanation as you'll get:
io的文档。Serializable(可串行化的)属性可能是你能得到的最好的解释:
The serialization runtime associates with each serializable class a version number, called a serialVersionUID, which 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
. A serializable class can declare its own serialVersionUID explicitly by declaring a field named "serialVersionUID
" that must be static, final, and of typelong
:序列化运行时与每个serializable类关联一个版本号(称为serialVersionUID),该版本号在反序列化过程中用于验证序列化对象的发送方和接收方是否为该对象加载了与序列化兼容的类。如果接收方为具有不同的serialVersionUID的对象装载了一个类,而不是相应的发送方的类,那么反序列化将导致一个InvalidClassException。一个serializable类可以通过声明一个名为“serialVersionUID”的字段来显式地声明它自己的serialVersionUID,这个字段必须是静态的、最终的,并且类型为long:
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java(TM) Object Serialization Specification. However, it is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected
InvalidClassExceptions
during deserialization. Therefore, to guarantee a consistent serialVersionUID value across different java compiler implementations, a serializable class must declare an explicit serialVersionUID value. It is also strongly advised that explicit serialVersionUID declarations use the private modifier where possible, since such declarations apply only to the immediately declaring class--serialVersionUID fields are not useful as inherited members.如果serializable类没有显式声明serialVersionUID,那么序列化运行时将根据类的各个方面计算该类的默认serialVersionUID值,如Java(TM)对象序列化规范所描述的那样。但是,强烈建议所有可序列化的类显式声明serialVersionUID值,因为默认的serialVersionUID计算对根据编译器实现而变化的类细节高度敏感,因此可能会在反序列化期间导致意外的无效classexceptions。因此,为了在不同的java编译器实现中保证一致的serialVersionUID值,serializable类必须声明一个显式的serialVersionUID值。还强烈建议显式的serialVersionUID声明在可能的情况下使用私有修饰符,因为这样的声明只适用于立即声明的类——serialVersionUID字段对继承成员没有用处。
#2
411
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 about de-serializing a form object), then you can ignore this.
如果您只是因为实现的原因而进行序列化(例如,如果您为HTTPSession进行序列化,谁会关心呢……)如果它被存储或没有被存储,您可能不关心对表单对象进行反序列化),那么您可以忽略它。
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.
如果您实际使用的是序列化,那么只有计划直接使用序列化存储和检索对象时才会有问题。serialVersionUID表示类版本,如果类的当前版本与以前版本不向后兼容,则应该对其进行递增。
Most of the time, you will probably not use serialization directly. If this is the case, generate a default serializable uid by clicking the quick fix option and don't worry about it.
大多数时候,您可能不会直接使用序列化。如果是这样,通过单击quick fix选项生成一个默认的serializable uid,不要担心这个问题。
#3
255
I can't pass up this opportunity to plug Josh Bloch's book Effective Java (2nd Edition). Chapter 11 is an indispensible resource on Java serialization.
我不能错过这个机会来介绍Josh Bloch的《有效Java》(第二版)。第11章是Java序列化不可缺少的资源。
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).
根据Josh的说法,自动生成的UID是基于类名、实现的接口以及所有公共和受保护的成员生成的。以任何方式更改这些内容都将更改serialVersionUID。因此,只有当您确信类的一个版本不会被序列化时,您才需要去处理它们(跨进程或稍后从存储中检索)。
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 adding writeObject
and readObject
methods - see Serializable
javadoc or aforementioned chapter 11.)
现在如果你忽略他们,找到后,你需要更改类在某种程度上,但保持兼容性w /旧版本的类,您可以使用JDK工具serialver生成serialVersionUID的旧课,并显式地设置在新类。(根据您的更改,您可能还需要通过添加writeObject和readObject方法来实现自定义序列化——请参阅Serializable javadoc或前面提到的第11章。)
#4
118
You can tell Eclipse to ignore these serialVersionUID warnings:
您可以告诉Eclipse忽略这些serialVersionUID警告:
Window > Preferences > Java > Compiler > Errors / Warnings > Potential Programming Problems
窗口>首选项> Java >编译器>错误/警告>潜在的编程问题
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:
如果您不知道,在这一节中您可以启用许多其他警告(甚至有一些报告为错误),许多警告是非常有用的:
- Potential Programming Problems: Possible accidental boolean assignment
- 潜在的编程问题:可能的偶然布尔赋值
- Potential Programming Problems: Null pointer access
- 潜在的编程问题:空指针访问
- Unnecessary code: Local variable is never read
- 不必要的代码:永远不会读取本地变量
- Unnecessary code: Redundant null check
- 不必要的代码:冗余零检查
- Unnecessary code: Unnecessary cast or 'instanceof'
- 不必要的代码:不必要的转换或“instanceof”
and many more.
和许多更多。
#5
94
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.
serialVersionUID有助于对序列化数据进行版本控制。它的值在序列化时与数据一起存储。反序列化时,检查相同的版本,以查看序列化数据如何匹配当前代码。
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).
如果您想要对数据进行版本化,您通常从一个serialVersionUID为0开始,并将其与修改序列化数据(添加或删除非瞬态字段)的类的每个结构更改结合起来。
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 the serialVersionUID
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.
内置的反序列化机制(in.defaultReadObject())将拒绝从旧版本的数据中反序列化。但是如果需要,可以定义自己的readObject()函数,它可以读取旧数据。然后,该自定义代码可以检查serialVersionUID,以了解数据所在的版本,并决定如何反序列化它。如果您存储的序列化数据保存在代码的几个版本中,那么这种版本控制技术是非常有用的。
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.
在这种情况下,您对保持向后兼容性不感兴趣。您只需要确保正在通信的代码基确实具有相关类的相同版本。为了方便这样的检查,您必须像以前一样维护serialVersionUID,并且在对类进行更改时不要忘记更新它。
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.
如果您忘记更新字段,您可能会得到一个具有不同结构但具有相同serialVersionUID的类的两个不同版本。如果发生这种情况,默认机制(in.defaultReadObject())将不会检测到任何差异,并试图去序列化不兼容的数据。现在,您可能会得到一个神秘的运行时错误或静默失败(空字段)。这些类型的错误可能很难找到。
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.
因此,为了帮助实现这个usecase, Java平台提供了一种选择,即不手动设置serialVersionUID。相反,类结构的散列将在编译时生成并作为id使用。该机制将确保您从未拥有具有相同id的不同类结构,因此您不会得到上述难以跟踪的运行时序列化失败。
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.
但是自动生成id策略也有其背后的原因。也就是说,相同类的生成id在编译器之间可能存在差异(如Jon Skeet上面提到的)。因此,如果您在使用不同编译器编译的代码之间通信序列化的数据,建议您还是手工维护id。
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.
如果您与前面提到的第一个用例中的数据向后兼容,您可能还需要自己维护id。这样做是为了获得可读的id,并更好地控制它们何时以及如何更改。
#6
60
What is a serialVersionUID and why should I use it?
什么是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.
SerialVersionUID是每个类的惟一标识符,JVM使用它来比较类的版本,确保在序列化期间加载序列化期间使用的类。
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.
指定一个可以提供更多的控制,但是如果不指定,JVM会生成一个。生成的值在不同的编译器之间可能存在差异。此外,有时出于某种原因,您只是希望禁止对旧的序列化对象进行反序列化[向后不兼容性],在这种情况下,您只需更改serialVersionUID。
Java docs says:
Java文档说:
"the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected InvalidClassExceptions during deserialization".
“默认的serialVersionUID计算对根据编译器实现而变化的类细节非常敏感,因此会在反序列化过程中导致意外的无效classexceptions”。
You must declare serialVersionUID because it give us more control.
您必须声明serialVersionUID,因为它提供了更多的控制。
This article has some good points on the topic.
这篇文章在这个话题上有一些不错的观点。
#7
39
Original question has asked for 'why is it important' and 'example' where this Serial Version ID
would be useful. Well I have found one.
最初的问题是“为什么它很重要”和“示例”,在这里这个串行版本ID很有用。我找到了一个。
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 the Car
class is modified by adding a new field. Later on, when you try to read (i.e. deserialize) the flattened Car
object, you get the java.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 your serialVersionUID
(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:
假设您创建了一个Car类,实例化它,并将它写到对象流中。扁平的car对象在文件系统中驻留了一段时间。同时,如果通过添加一个新字段来修改Car类。稍后,当您尝试读取(即反序列化)扁平的Car对象时,您将获得java.io。无效classexception -因为所有可序列化的类都被自动赋予唯一的标识符。当类的标识符不等于扁平对象的标识符时,抛出此异常。如果您真的考虑它,由于添加了新字段,会抛出异常。您可以通过声明一个显式serialVersionUID来控制版本控制,从而避免抛出这个异常。在显式地声明serialVersionUID时,还有一个小的性能优势(因为不需要计算)。因此,最好的实践是,在创建serialVersionUID之后,立即将它们添加到您的Serializable类中,如下所示:
public class Car {
static final long serialVersionUID = 1L; //assign a long value
}
#8
31
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.
如果您在一个类上收到这个警告,您从来没有考虑过序列化,也没有声明自己实现了Serializable,这通常是因为您继承了一个超类,它实现了Serializable。通常情况下,最好委托给这样的对象,而不是使用继承。
So, instead of
因此,而不是
public class MyExample extends ArrayList<String> {
public MyExample() {
super();
}
...
}
do
做
public class MyExample {
private List<String> myList;
public MyExample() {
this.myList = new ArrayList<String>();
}
...
}
and in the relevant methods call myList.foo()
instead of this.foo()
(or super.foo()
). (This does not fit in all cases, but still quite often.)
在相关的方法中调用myList.foo()而不是this.foo()(或super.foo())。(这并不适用于所有情况,但仍然经常适用。)
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.)
我经常看到人们扩展JFrame之类的东西,而实际上他们只需要委托给它。(这也有助于在IDE中自动完成,因为JFrame有数百个方法,当您想在类上调用自定义方法时不需要这些方法)。
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)不可避免的情况是,当您从AbstractAction(通常在匿名类中)扩展时,只添加actionmed方法。我认为在这种情况下不应该有任何警告(因为通常您无法可靠地序列化和反序列化此类匿名类,无论如何都要面对类的不同版本),但我不确定编译器如何识别这一点。
#9
28
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.
如果您永远都不需要将对象序列化为字节数组并发送/存储它们,那么您不必为此担心。如果您这样做,那么您必须考虑您的serialVersionUID,因为对象的反序列化器会将它与它的classloader所具有的对象版本相匹配。在Java语言规范中阅读更多有关它的信息。
#10
17
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)
不用麻烦了,默认的计算非常好,对于999999%的情况都足够了。如果您遇到问题,您可以(如前所述)引入UID作为需求arrise(非常不可能)
#11
17
To understand the significance of field serialVersionUID, one should understand how Serialization/Deserialization works.
要理解字段serialVersionUID的意义,应该了解序列化/反序列化是如何工作的。
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.
当序列化的类对象被序列化时,Java运行时关联一个串行版本号。(称为serialVersionUID)具有这个序列化的对象。在反序列化此序列化对象时,Java运行时将序列化对象的serialVersionUID与类的serialVersionUID匹配。如果两者都相等,那么只有它继续进行反序列化的进一步过程,否则抛出无效classexception。
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).
因此,为了使序列化/反序列化过程成功,序列化对象的serialVersionUID必须与类的serialVersionUID相等。以防如果程序员指定serialVersionUID值显式地在相关的程序相同的值将被序列化的对象和类,无论序列化和deserialzation平台(例如序列化可以在平台(如windows使用太阳或JVM女士和反序列化使用活力在不同平台Linux 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运行时将使用它自己的算法来计算它。这种serialVersionUID计算算法因JRE的不同而不同。序列化对象的环境也可能使用一个JRE(例如:SUN JVM),反序列化发生的环境也可能使用Linux JVM (zing)。在这种情况下,与序列化对象相关联的serialVersionUID将与在反序列化环境中计算的类的serialVersionUID不同。反过来,反序列化也不会成功。因此,为了避免这种情况/问题,程序员必须始终指定serialVersionUID为Serializable类。
#12
15
As for an example where the missing serialVersionUID might cause a problem:
例如,丢失的serialVersionUID可能导致问题:
I'm working on this Java EE application that is composed of a Web module that uses an EJB
module. The web module calls the EJB
module remotely and passes a POJO
that implements Serializable
as an argument.
我正在开发这个Java EE应用程序,它由使用EJB模块的Web模块组成。web模块远程调用EJB模块,并传递实现Serializable的POJO作为参数。
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.
这个POJO的类被封装在EJB jar中,在web模块的web - inf /lib中有自己的jar。它们实际上是同一个类,但是当我打包EJB模块时,我将这个POJO的jar解压缩到EJB模块中。
The call to the EJB
was failing with the Exception below because I hadn't declared its serialVersionUID
:
对EJB的调用失败了,因为我没有声明它的serialVersionUID:
Caused by: java.io.IOException: Mismatched serialization UIDs : Source
(Rep.
IDRMI:com.hordine.pedra.softbudget.domain.Budget:5CF7CE11E6810A36:04A3FEBED5DA4588)
= 04A3FEBED5DA4588 whereas Target (Rep. ID RMI:com.hordine.pedra.softbudget.domain.Budget:7AF5ED7A7CFDFF31:6227F23FA74A9A52)
= 6227F23FA74A9A52
#13
11
I generally use serialVersionUID
in one context: When I know it will be leaving the context of the Java VM.
我通常在一个上下文中使用serialVersionUID:当我知道它将离开Java VM的上下文中时。
I would know this when I to use ObjectInputStream
and ObjectOutputStream
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 example HttpSession
the session data can remain even during a restart and upgrade of the application server.
当我在应用程序中使用ObjectInputStream和ObjectOutputStream时,或者如果我知道我使用的库/框架将使用它,我就会知道这一点。serialVersionID确保不同版本或供应商的不同Java VM能够正确地相互操作,或者如果在VM(例如HttpSession)之外存储和检索会话数据,即使在重新启动和升级应用服务器时,会话数据也可以保持不变。
For all other cases, I use
对于所有其他情况,我使用。
@SuppressWarnings("serial")
since most of the time the default serialVersionUID
is sufficient. This includes Exception
, HttpServlet
.
因为大多数时候默认的serialVersionUID是足够的。这包括例外,HttpServlet。
#14
9
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.
字段数据表示存储在类中的一些信息。类实现Serializable接口,因此eclipse会自动提供serialVersionUID字段声明。我们从值1开始。
If you don't want that warning to come, use this:
如果你不希望得到这样的警告,可以这样做:
@SuppressWarnings("serial")
#15
8
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.
如果CheckStyle能够验证一个实现Serializable的类的serialVersionUID具有一个很好的值,即它与串行版本id生成器生成的值相匹配,那就更好了。如果您有一个项目有很多可序列化的dto,例如,请记住删除现有的serialVersionUID并重新生成它是一种痛苦,而目前验证这一点的唯一方法(我知道)是为每个类重新生成并与旧的类进行比较。这是非常非常痛苦的。
#16
8
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
SerialVersionUID用于对象的版本控制。您还可以在类文件中指定serialVersionUID。不指定serialVersionUID的后果是,当您添加或修改类中的任何字段时,已经序列化的类将无法恢复,因为为新类和旧序列化对象生成的serialVersionUID将不同。Java序列化过程依赖于正确的serialVersionUID来恢复序列化对象的状态并抛出Java .io。在串行版本号不匹配的情况下失效classexception
Read more: http://javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html#ixzz3VQxnpOPZ
阅读更多:http://javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html # ixzz3VQxnpOPZ
#17
7
First I need to explain the serialization.
Serialization allows to convert the object to stream,for sending that object over the network OR Save to file OR save into DB for letter usage.
首先我需要解释序列化。序列化允许将对象转换为流,以便通过网络发送该对象,或将其保存为文件,或将其保存为DB以备信件使用。
There are some rules for serialization.
序列化有一些规则。
-
An object is serializable only if its class or its superclass implements the Serializable interface
对象只有在其类或超类实现serializable接口时才可序列化
-
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
对象是serializable(本身实现serializable接口),即使它的超类不是。然而,serializable类的层次结构中的第一个超类(它没有实现serializable接口)必须有一个无arg构造函数。如果违反了这一点,readObject()将生成java.io。InvalidClassException在运行时
-
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).
暂态字段(带有暂态修饰符)不是序列化的(即。,而不是保存或恢复)。实现Serializable的类必须标记不支持序列化的类的临时字段(例如文件流)。
-
Static fields (with static modifier) are Not serialized.
静态字段(带有静态修饰符)没有序列化。
When Object is Serialized JAVA Runtime Associates the serial version number that is called the serialVersionID.
当对象被序列化时,JAVA运行时将被称为serialVersionID的串行版本号关联起来。
Where we need serialVersionID : During the deserialization to verify that sender and receiver are compatible with respect to serialization.If receiver loaded the class with different serialVersionID then deserialization will end with InvalidClassCastException.
A serializable class can declare its own serialVersionUID explicitly by declaring a field named “serialVersionUID” that must be static, final, and of type long:.
我们需要serialVersionID的地方是:在反序列化期间,验证发送方和接收方与序列化兼容。如果接收者使用不同的serialVersionID加载类,那么反序列化将以InvalidClassCastException结束。serializable类可以通过声明一个名为“serialVersionUID”的字段来显式地声明它自己的serialVersionUID,该字段必须是静态的、最终的,并且类型为long:。
Let's try this with an example.
让我们用一个例子来尝试一下。
import java.io.Serializable;
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private String empname;
private byte empage;
public String getEmpName() {
return name;
}
public void setEmpName(String empname) {
this.empname = empname;
}
public byte getEmpAge() {
return empage;
}
public void setEmpAge(byte empage) {
this.empage = empage;
}
public String whoIsThis() {
StringBuffer employee = new StringBuffer();
employee.append(getEmpName()).append(" is ).append(getEmpAge()).append("
years old "));
return employee.toString();
}
}
Create Serialize Object
创建序列化对象
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Writer {
public static void main(String[] args) throws IOException {
Employee employee = new Employee();
employee.setEmpName("Jagdish");
employee.setEmpAge((byte) 30);
FileOutputStream fout = new
FileOutputStream("/users/Jagdish.vala/employee.obj");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(employee);
oos.close();
System.out.println("Process complete");
}
}
Deserializ the object
Deserializ对象
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Reader {
public static void main(String[] args) throws ClassNotFoundException,
IOException {
Employee employee = new Employee();
FileInputStream fin = new
FileInputStream("/users/Jagdish.vala/employee.obj");
ObjectInputStream ois = new ObjectInputStream(fin);
employee = (Employee) ois.readObject();
ois.close();
System.out.println(employee.whoIsThis());
}
}
NOTE: Now change the serialVersionUID of the Employee class and save:
注意:现在更改Employee类的serialVersionUID并保存:
private static final long serialVersionUID = **4L**;
And execute the Reader class. Not to execute the Writer class and you will get the exception.
并执行Reader类。不执行写入器类,您将得到异常。
Exception in thread "main" java.io.InvalidClassException:
com.jagdish.vala.java.serialVersion.Employee; local class incompatible:
stream classdesc serialVersionUID = 1, local class serialVersionUID = 4
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at com.krishantha.sample.java.serialVersion.Reader.main(Reader.java:14)
#18
5
Why use SerialVersionUID
inside Serializable
class in Java?
为什么在Java中使用SerialVersionUID ?
During serialization
, Java runtime creates a version number for a class, so that it can de-serialize it later. This version number is known as SerialVersionUID
in Java.
在序列化期间,Java运行时为一个类创建一个版本号,以便以后可以反序列化它。这个版本号在Java中称为SerialVersionUID。
SerialVersionUID
is used to version serialized data. You can only de-serialize a class if it's SerialVersionUID
matches with the serialized instance. When we don't declare SerialVersionUID
in our class, Java runtime generates it for us but its not recommended. It's recommended to declare SerialVersionUID
as private static final long
variable to avoid default mechanism.
SerialVersionUID用于对序列化数据进行版本化。只有当类的SerialVersionUID与序列化实例匹配时,才可以反序列化。当我们在类中不声明SerialVersionUID时,Java运行时为我们生成它,但不建议这样做。建议将SerialVersionUID声明为私有静态最终长变量,以避免默认机制。
When you declare a class as Serializable
by implementing marker interface java.io.Serializable
, Java runtime persist instance of that class into disk by using default Serialization mechanism, provided you have not customized the process using Externalizable
interface.
通过实现marker接口java.io将类声明为Serializable。如果您没有使用Externalizable接口定制进程,那么可以使用默认的序列化机制将该类的Java运行时实例持久化到磁盘中。
see also Why use SerialVersionUID inside Serializable class in Java
还要了解为什么在Java中的Serializable类中使用SerialVersionUID
Code : javassist.SerialVersionUID
代码:javassist.SerialVersionUID
#19
3
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:
如果你想修改大量的类没有serialVersionUID设置在第一时间同时保持兼容旧的类,工具如IntelliJ Idea,Eclipse生成随机数时不足和不工作一堆文件。我提出了如下的bash脚本(对于Windows用户来说,我很抱歉,考虑购买一个Mac或转换到Linux),以便轻松地修改serialVersionUID问题:
base_dir=$(pwd)
src_dir=$base_dir/src/main/java
ic_api_cp=$base_dir/target/classes
while read f
do
clazz=${f//\//.}
clazz=${clazz/%.java/}
seruidstr=$(serialver -classpath $ic_api_cp $clazz | cut -d ':' -f 2 | sed -e 's/^\s\+//')
perl -ni.bak -e "print $_; printf qq{%s\n}, q{ private $seruidstr} if /public class/" $src_dir/$f
done
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:
保存这个脚本,比如add_serialVersionUID。sh ~ / bin。然后在Maven或Gradle项目的根目录中运行它,比如:
add_serialVersionUID.sh < myJavaToAmend.lst
This .lst includes the list of java files to add the serialVersionUID in the following format:
这个.lst包含了以以下格式添加serialVersionUID的java文件列表:
com/abc/ic/api/model/domain/item/BizOrderTransDO.java
com/abc/ic/api/model/domain/item/CardPassFeature.java
com/abc/ic/api/model/domain/item/CategoryFeature.java
com/abc/ic/api/model/domain/item/GoodsFeature.java
com/abc/ic/api/model/domain/item/ItemFeature.java
com/abc/ic/api/model/domain/item/ItemPicUrls.java
com/abc/ic/api/model/domain/item/ItemSkuDO.java
com/abc/ic/api/model/domain/serve/ServeCategoryFeature.java
com/abc/ic/api/model/domain/serve/ServeFeature.java
com/abc/ic/api/model/param/depot/DepotItemDTO.java
com/abc/ic/api/model/param/depot/DepotItemQueryDTO.java
com/abc/ic/api/model/param/depot/InDepotDTO.java
com/abc/ic/api/model/param/depot/OutDepotDTO.java
This script uses the JDK serialVer tool under hood. So make sure your $JAVA_HOME/bin is in the PATH.
这个脚本使用了hood下的JDK serialVer工具。因此,确保您的$JAVA_HOME/bin在路径中。
#20
3
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 :
这个问题在《有效Java》中被Joshua Bloch很好地记录了下来。一本好书,一本必读的书。我将概述以下原因:
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.
序列化运行时为每个serializable类提供了一个名为串行版本的数字。这个数字叫做serialVersionUID。这个数字后面有一些数学运算,它基于类中定义的字段/方法。对于同一个类,每次都会生成相同的版本。在反序列化过程中使用这个数字来验证序列化对象的发送方和接收方已经为该对象加载了与序列化兼容的类。如果接收方已经为具有与相应发送方的类不同的serialVersionUID的对象加载了一个类,那么反序列化将导致无效classexception。
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.
如果类是可序列化的,您还可以显式声明自己的serialVersionUID,方法是声明一个名为“serialVersionUID”的字段,该字段必须是静态的、最终的、类型为long的。大多数IDE都像Eclipse一样帮助您生成长字符串。
#21
2
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.
每当对象被序列化时,对象的类都有一个版本ID号。这个ID称为serialVersionUID,它是基于关于类结构的信息进行计算的。假设您创建了一个Employee类,并且它具有版本id #333(由JVM分配),现在当您将序列化该类的对象(假设Employee对象)时,JVM将为它分配UID为#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.
考虑一种情况——在将来,您需要编辑或更改类,在这种情况下,当您修改它时,JVM将为它分配一个新的UID(假设#444)。现在,当您试图反序列化employee对象时,JVM会将序列化对象的(employee对象)version ID(#333)与类i进行比较。e# 444(因为它被更改了)。相比之下,JVM会发现两个版本的UID都不同,因此反序列化将失败。因此,如果每个类的serialVersionID是由程序员自己定义的。即使类是在将来发展的,它也是一样的,因此JVM总是会发现类与序列化的对象是兼容的,即使类是被修改的。有关更多信息,请参阅HEAD FIRST JAVA的第14章。
#1
1875
The docs for java.io.Serializable
are probably about as good an explanation as you'll get:
io的文档。Serializable(可串行化的)属性可能是你能得到的最好的解释:
The serialization runtime associates with each serializable class a version number, called a serialVersionUID, which 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
. A serializable class can declare its own serialVersionUID explicitly by declaring a field named "serialVersionUID
" that must be static, final, and of typelong
:序列化运行时与每个serializable类关联一个版本号(称为serialVersionUID),该版本号在反序列化过程中用于验证序列化对象的发送方和接收方是否为该对象加载了与序列化兼容的类。如果接收方为具有不同的serialVersionUID的对象装载了一个类,而不是相应的发送方的类,那么反序列化将导致一个InvalidClassException。一个serializable类可以通过声明一个名为“serialVersionUID”的字段来显式地声明它自己的serialVersionUID,这个字段必须是静态的、最终的,并且类型为long:
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java(TM) Object Serialization Specification. However, it is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected
InvalidClassExceptions
during deserialization. Therefore, to guarantee a consistent serialVersionUID value across different java compiler implementations, a serializable class must declare an explicit serialVersionUID value. It is also strongly advised that explicit serialVersionUID declarations use the private modifier where possible, since such declarations apply only to the immediately declaring class--serialVersionUID fields are not useful as inherited members.如果serializable类没有显式声明serialVersionUID,那么序列化运行时将根据类的各个方面计算该类的默认serialVersionUID值,如Java(TM)对象序列化规范所描述的那样。但是,强烈建议所有可序列化的类显式声明serialVersionUID值,因为默认的serialVersionUID计算对根据编译器实现而变化的类细节高度敏感,因此可能会在反序列化期间导致意外的无效classexceptions。因此,为了在不同的java编译器实现中保证一致的serialVersionUID值,serializable类必须声明一个显式的serialVersionUID值。还强烈建议显式的serialVersionUID声明在可能的情况下使用私有修饰符,因为这样的声明只适用于立即声明的类——serialVersionUID字段对继承成员没有用处。
#2
411
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 about de-serializing a form object), then you can ignore this.
如果您只是因为实现的原因而进行序列化(例如,如果您为HTTPSession进行序列化,谁会关心呢……)如果它被存储或没有被存储,您可能不关心对表单对象进行反序列化),那么您可以忽略它。
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.
如果您实际使用的是序列化,那么只有计划直接使用序列化存储和检索对象时才会有问题。serialVersionUID表示类版本,如果类的当前版本与以前版本不向后兼容,则应该对其进行递增。
Most of the time, you will probably not use serialization directly. If this is the case, generate a default serializable uid by clicking the quick fix option and don't worry about it.
大多数时候,您可能不会直接使用序列化。如果是这样,通过单击quick fix选项生成一个默认的serializable uid,不要担心这个问题。
#3
255
I can't pass up this opportunity to plug Josh Bloch's book Effective Java (2nd Edition). Chapter 11 is an indispensible resource on Java serialization.
我不能错过这个机会来介绍Josh Bloch的《有效Java》(第二版)。第11章是Java序列化不可缺少的资源。
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).
根据Josh的说法,自动生成的UID是基于类名、实现的接口以及所有公共和受保护的成员生成的。以任何方式更改这些内容都将更改serialVersionUID。因此,只有当您确信类的一个版本不会被序列化时,您才需要去处理它们(跨进程或稍后从存储中检索)。
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 adding writeObject
and readObject
methods - see Serializable
javadoc or aforementioned chapter 11.)
现在如果你忽略他们,找到后,你需要更改类在某种程度上,但保持兼容性w /旧版本的类,您可以使用JDK工具serialver生成serialVersionUID的旧课,并显式地设置在新类。(根据您的更改,您可能还需要通过添加writeObject和readObject方法来实现自定义序列化——请参阅Serializable javadoc或前面提到的第11章。)
#4
118
You can tell Eclipse to ignore these serialVersionUID warnings:
您可以告诉Eclipse忽略这些serialVersionUID警告:
Window > Preferences > Java > Compiler > Errors / Warnings > Potential Programming Problems
窗口>首选项> Java >编译器>错误/警告>潜在的编程问题
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:
如果您不知道,在这一节中您可以启用许多其他警告(甚至有一些报告为错误),许多警告是非常有用的:
- Potential Programming Problems: Possible accidental boolean assignment
- 潜在的编程问题:可能的偶然布尔赋值
- Potential Programming Problems: Null pointer access
- 潜在的编程问题:空指针访问
- Unnecessary code: Local variable is never read
- 不必要的代码:永远不会读取本地变量
- Unnecessary code: Redundant null check
- 不必要的代码:冗余零检查
- Unnecessary code: Unnecessary cast or 'instanceof'
- 不必要的代码:不必要的转换或“instanceof”
and many more.
和许多更多。
#5
94
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.
serialVersionUID有助于对序列化数据进行版本控制。它的值在序列化时与数据一起存储。反序列化时,检查相同的版本,以查看序列化数据如何匹配当前代码。
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).
如果您想要对数据进行版本化,您通常从一个serialVersionUID为0开始,并将其与修改序列化数据(添加或删除非瞬态字段)的类的每个结构更改结合起来。
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 the serialVersionUID
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.
内置的反序列化机制(in.defaultReadObject())将拒绝从旧版本的数据中反序列化。但是如果需要,可以定义自己的readObject()函数,它可以读取旧数据。然后,该自定义代码可以检查serialVersionUID,以了解数据所在的版本,并决定如何反序列化它。如果您存储的序列化数据保存在代码的几个版本中,那么这种版本控制技术是非常有用的。
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.
在这种情况下,您对保持向后兼容性不感兴趣。您只需要确保正在通信的代码基确实具有相关类的相同版本。为了方便这样的检查,您必须像以前一样维护serialVersionUID,并且在对类进行更改时不要忘记更新它。
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.
如果您忘记更新字段,您可能会得到一个具有不同结构但具有相同serialVersionUID的类的两个不同版本。如果发生这种情况,默认机制(in.defaultReadObject())将不会检测到任何差异,并试图去序列化不兼容的数据。现在,您可能会得到一个神秘的运行时错误或静默失败(空字段)。这些类型的错误可能很难找到。
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.
因此,为了帮助实现这个usecase, Java平台提供了一种选择,即不手动设置serialVersionUID。相反,类结构的散列将在编译时生成并作为id使用。该机制将确保您从未拥有具有相同id的不同类结构,因此您不会得到上述难以跟踪的运行时序列化失败。
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.
但是自动生成id策略也有其背后的原因。也就是说,相同类的生成id在编译器之间可能存在差异(如Jon Skeet上面提到的)。因此,如果您在使用不同编译器编译的代码之间通信序列化的数据,建议您还是手工维护id。
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.
如果您与前面提到的第一个用例中的数据向后兼容,您可能还需要自己维护id。这样做是为了获得可读的id,并更好地控制它们何时以及如何更改。
#6
60
What is a serialVersionUID and why should I use it?
什么是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.
SerialVersionUID是每个类的惟一标识符,JVM使用它来比较类的版本,确保在序列化期间加载序列化期间使用的类。
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.
指定一个可以提供更多的控制,但是如果不指定,JVM会生成一个。生成的值在不同的编译器之间可能存在差异。此外,有时出于某种原因,您只是希望禁止对旧的序列化对象进行反序列化[向后不兼容性],在这种情况下,您只需更改serialVersionUID。
Java docs says:
Java文档说:
"the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected InvalidClassExceptions during deserialization".
“默认的serialVersionUID计算对根据编译器实现而变化的类细节非常敏感,因此会在反序列化过程中导致意外的无效classexceptions”。
You must declare serialVersionUID because it give us more control.
您必须声明serialVersionUID,因为它提供了更多的控制。
This article has some good points on the topic.
这篇文章在这个话题上有一些不错的观点。
#7
39
Original question has asked for 'why is it important' and 'example' where this Serial Version ID
would be useful. Well I have found one.
最初的问题是“为什么它很重要”和“示例”,在这里这个串行版本ID很有用。我找到了一个。
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 the Car
class is modified by adding a new field. Later on, when you try to read (i.e. deserialize) the flattened Car
object, you get the java.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 your serialVersionUID
(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:
假设您创建了一个Car类,实例化它,并将它写到对象流中。扁平的car对象在文件系统中驻留了一段时间。同时,如果通过添加一个新字段来修改Car类。稍后,当您尝试读取(即反序列化)扁平的Car对象时,您将获得java.io。无效classexception -因为所有可序列化的类都被自动赋予唯一的标识符。当类的标识符不等于扁平对象的标识符时,抛出此异常。如果您真的考虑它,由于添加了新字段,会抛出异常。您可以通过声明一个显式serialVersionUID来控制版本控制,从而避免抛出这个异常。在显式地声明serialVersionUID时,还有一个小的性能优势(因为不需要计算)。因此,最好的实践是,在创建serialVersionUID之后,立即将它们添加到您的Serializable类中,如下所示:
public class Car {
static final long serialVersionUID = 1L; //assign a long value
}
#8
31
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.
如果您在一个类上收到这个警告,您从来没有考虑过序列化,也没有声明自己实现了Serializable,这通常是因为您继承了一个超类,它实现了Serializable。通常情况下,最好委托给这样的对象,而不是使用继承。
So, instead of
因此,而不是
public class MyExample extends ArrayList<String> {
public MyExample() {
super();
}
...
}
do
做
public class MyExample {
private List<String> myList;
public MyExample() {
this.myList = new ArrayList<String>();
}
...
}
and in the relevant methods call myList.foo()
instead of this.foo()
(or super.foo()
). (This does not fit in all cases, but still quite often.)
在相关的方法中调用myList.foo()而不是this.foo()(或super.foo())。(这并不适用于所有情况,但仍然经常适用。)
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.)
我经常看到人们扩展JFrame之类的东西,而实际上他们只需要委托给它。(这也有助于在IDE中自动完成,因为JFrame有数百个方法,当您想在类上调用自定义方法时不需要这些方法)。
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)不可避免的情况是,当您从AbstractAction(通常在匿名类中)扩展时,只添加actionmed方法。我认为在这种情况下不应该有任何警告(因为通常您无法可靠地序列化和反序列化此类匿名类,无论如何都要面对类的不同版本),但我不确定编译器如何识别这一点。
#9
28
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.
如果您永远都不需要将对象序列化为字节数组并发送/存储它们,那么您不必为此担心。如果您这样做,那么您必须考虑您的serialVersionUID,因为对象的反序列化器会将它与它的classloader所具有的对象版本相匹配。在Java语言规范中阅读更多有关它的信息。
#10
17
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)
不用麻烦了,默认的计算非常好,对于999999%的情况都足够了。如果您遇到问题,您可以(如前所述)引入UID作为需求arrise(非常不可能)
#11
17
To understand the significance of field serialVersionUID, one should understand how Serialization/Deserialization works.
要理解字段serialVersionUID的意义,应该了解序列化/反序列化是如何工作的。
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.
当序列化的类对象被序列化时,Java运行时关联一个串行版本号。(称为serialVersionUID)具有这个序列化的对象。在反序列化此序列化对象时,Java运行时将序列化对象的serialVersionUID与类的serialVersionUID匹配。如果两者都相等,那么只有它继续进行反序列化的进一步过程,否则抛出无效classexception。
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).
因此,为了使序列化/反序列化过程成功,序列化对象的serialVersionUID必须与类的serialVersionUID相等。以防如果程序员指定serialVersionUID值显式地在相关的程序相同的值将被序列化的对象和类,无论序列化和deserialzation平台(例如序列化可以在平台(如windows使用太阳或JVM女士和反序列化使用活力在不同平台Linux 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运行时将使用它自己的算法来计算它。这种serialVersionUID计算算法因JRE的不同而不同。序列化对象的环境也可能使用一个JRE(例如:SUN JVM),反序列化发生的环境也可能使用Linux JVM (zing)。在这种情况下,与序列化对象相关联的serialVersionUID将与在反序列化环境中计算的类的serialVersionUID不同。反过来,反序列化也不会成功。因此,为了避免这种情况/问题,程序员必须始终指定serialVersionUID为Serializable类。
#12
15
As for an example where the missing serialVersionUID might cause a problem:
例如,丢失的serialVersionUID可能导致问题:
I'm working on this Java EE application that is composed of a Web module that uses an EJB
module. The web module calls the EJB
module remotely and passes a POJO
that implements Serializable
as an argument.
我正在开发这个Java EE应用程序,它由使用EJB模块的Web模块组成。web模块远程调用EJB模块,并传递实现Serializable的POJO作为参数。
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.
这个POJO的类被封装在EJB jar中,在web模块的web - inf /lib中有自己的jar。它们实际上是同一个类,但是当我打包EJB模块时,我将这个POJO的jar解压缩到EJB模块中。
The call to the EJB
was failing with the Exception below because I hadn't declared its serialVersionUID
:
对EJB的调用失败了,因为我没有声明它的serialVersionUID:
Caused by: java.io.IOException: Mismatched serialization UIDs : Source
(Rep.
IDRMI:com.hordine.pedra.softbudget.domain.Budget:5CF7CE11E6810A36:04A3FEBED5DA4588)
= 04A3FEBED5DA4588 whereas Target (Rep. ID RMI:com.hordine.pedra.softbudget.domain.Budget:7AF5ED7A7CFDFF31:6227F23FA74A9A52)
= 6227F23FA74A9A52
#13
11
I generally use serialVersionUID
in one context: When I know it will be leaving the context of the Java VM.
我通常在一个上下文中使用serialVersionUID:当我知道它将离开Java VM的上下文中时。
I would know this when I to use ObjectInputStream
and ObjectOutputStream
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 example HttpSession
the session data can remain even during a restart and upgrade of the application server.
当我在应用程序中使用ObjectInputStream和ObjectOutputStream时,或者如果我知道我使用的库/框架将使用它,我就会知道这一点。serialVersionID确保不同版本或供应商的不同Java VM能够正确地相互操作,或者如果在VM(例如HttpSession)之外存储和检索会话数据,即使在重新启动和升级应用服务器时,会话数据也可以保持不变。
For all other cases, I use
对于所有其他情况,我使用。
@SuppressWarnings("serial")
since most of the time the default serialVersionUID
is sufficient. This includes Exception
, HttpServlet
.
因为大多数时候默认的serialVersionUID是足够的。这包括例外,HttpServlet。
#14
9
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.
字段数据表示存储在类中的一些信息。类实现Serializable接口,因此eclipse会自动提供serialVersionUID字段声明。我们从值1开始。
If you don't want that warning to come, use this:
如果你不希望得到这样的警告,可以这样做:
@SuppressWarnings("serial")
#15
8
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.
如果CheckStyle能够验证一个实现Serializable的类的serialVersionUID具有一个很好的值,即它与串行版本id生成器生成的值相匹配,那就更好了。如果您有一个项目有很多可序列化的dto,例如,请记住删除现有的serialVersionUID并重新生成它是一种痛苦,而目前验证这一点的唯一方法(我知道)是为每个类重新生成并与旧的类进行比较。这是非常非常痛苦的。
#16
8
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
SerialVersionUID用于对象的版本控制。您还可以在类文件中指定serialVersionUID。不指定serialVersionUID的后果是,当您添加或修改类中的任何字段时,已经序列化的类将无法恢复,因为为新类和旧序列化对象生成的serialVersionUID将不同。Java序列化过程依赖于正确的serialVersionUID来恢复序列化对象的状态并抛出Java .io。在串行版本号不匹配的情况下失效classexception
Read more: http://javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html#ixzz3VQxnpOPZ
阅读更多:http://javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html # ixzz3VQxnpOPZ
#17
7
First I need to explain the serialization.
Serialization allows to convert the object to stream,for sending that object over the network OR Save to file OR save into DB for letter usage.
首先我需要解释序列化。序列化允许将对象转换为流,以便通过网络发送该对象,或将其保存为文件,或将其保存为DB以备信件使用。
There are some rules for serialization.
序列化有一些规则。
-
An object is serializable only if its class or its superclass implements the Serializable interface
对象只有在其类或超类实现serializable接口时才可序列化
-
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
对象是serializable(本身实现serializable接口),即使它的超类不是。然而,serializable类的层次结构中的第一个超类(它没有实现serializable接口)必须有一个无arg构造函数。如果违反了这一点,readObject()将生成java.io。InvalidClassException在运行时
-
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).
暂态字段(带有暂态修饰符)不是序列化的(即。,而不是保存或恢复)。实现Serializable的类必须标记不支持序列化的类的临时字段(例如文件流)。
-
Static fields (with static modifier) are Not serialized.
静态字段(带有静态修饰符)没有序列化。
When Object is Serialized JAVA Runtime Associates the serial version number that is called the serialVersionID.
当对象被序列化时,JAVA运行时将被称为serialVersionID的串行版本号关联起来。
Where we need serialVersionID : During the deserialization to verify that sender and receiver are compatible with respect to serialization.If receiver loaded the class with different serialVersionID then deserialization will end with InvalidClassCastException.
A serializable class can declare its own serialVersionUID explicitly by declaring a field named “serialVersionUID” that must be static, final, and of type long:.
我们需要serialVersionID的地方是:在反序列化期间,验证发送方和接收方与序列化兼容。如果接收者使用不同的serialVersionID加载类,那么反序列化将以InvalidClassCastException结束。serializable类可以通过声明一个名为“serialVersionUID”的字段来显式地声明它自己的serialVersionUID,该字段必须是静态的、最终的,并且类型为long:。
Let's try this with an example.
让我们用一个例子来尝试一下。
import java.io.Serializable;
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private String empname;
private byte empage;
public String getEmpName() {
return name;
}
public void setEmpName(String empname) {
this.empname = empname;
}
public byte getEmpAge() {
return empage;
}
public void setEmpAge(byte empage) {
this.empage = empage;
}
public String whoIsThis() {
StringBuffer employee = new StringBuffer();
employee.append(getEmpName()).append(" is ).append(getEmpAge()).append("
years old "));
return employee.toString();
}
}
Create Serialize Object
创建序列化对象
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Writer {
public static void main(String[] args) throws IOException {
Employee employee = new Employee();
employee.setEmpName("Jagdish");
employee.setEmpAge((byte) 30);
FileOutputStream fout = new
FileOutputStream("/users/Jagdish.vala/employee.obj");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(employee);
oos.close();
System.out.println("Process complete");
}
}
Deserializ the object
Deserializ对象
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Reader {
public static void main(String[] args) throws ClassNotFoundException,
IOException {
Employee employee = new Employee();
FileInputStream fin = new
FileInputStream("/users/Jagdish.vala/employee.obj");
ObjectInputStream ois = new ObjectInputStream(fin);
employee = (Employee) ois.readObject();
ois.close();
System.out.println(employee.whoIsThis());
}
}
NOTE: Now change the serialVersionUID of the Employee class and save:
注意:现在更改Employee类的serialVersionUID并保存:
private static final long serialVersionUID = **4L**;
And execute the Reader class. Not to execute the Writer class and you will get the exception.
并执行Reader类。不执行写入器类,您将得到异常。
Exception in thread "main" java.io.InvalidClassException:
com.jagdish.vala.java.serialVersion.Employee; local class incompatible:
stream classdesc serialVersionUID = 1, local class serialVersionUID = 4
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
at com.krishantha.sample.java.serialVersion.Reader.main(Reader.java:14)
#18
5
Why use SerialVersionUID
inside Serializable
class in Java?
为什么在Java中使用SerialVersionUID ?
During serialization
, Java runtime creates a version number for a class, so that it can de-serialize it later. This version number is known as SerialVersionUID
in Java.
在序列化期间,Java运行时为一个类创建一个版本号,以便以后可以反序列化它。这个版本号在Java中称为SerialVersionUID。
SerialVersionUID
is used to version serialized data. You can only de-serialize a class if it's SerialVersionUID
matches with the serialized instance. When we don't declare SerialVersionUID
in our class, Java runtime generates it for us but its not recommended. It's recommended to declare SerialVersionUID
as private static final long
variable to avoid default mechanism.
SerialVersionUID用于对序列化数据进行版本化。只有当类的SerialVersionUID与序列化实例匹配时,才可以反序列化。当我们在类中不声明SerialVersionUID时,Java运行时为我们生成它,但不建议这样做。建议将SerialVersionUID声明为私有静态最终长变量,以避免默认机制。
When you declare a class as Serializable
by implementing marker interface java.io.Serializable
, Java runtime persist instance of that class into disk by using default Serialization mechanism, provided you have not customized the process using Externalizable
interface.
通过实现marker接口java.io将类声明为Serializable。如果您没有使用Externalizable接口定制进程,那么可以使用默认的序列化机制将该类的Java运行时实例持久化到磁盘中。
see also Why use SerialVersionUID inside Serializable class in Java
还要了解为什么在Java中的Serializable类中使用SerialVersionUID
Code : javassist.SerialVersionUID
代码:javassist.SerialVersionUID
#19
3
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:
如果你想修改大量的类没有serialVersionUID设置在第一时间同时保持兼容旧的类,工具如IntelliJ Idea,Eclipse生成随机数时不足和不工作一堆文件。我提出了如下的bash脚本(对于Windows用户来说,我很抱歉,考虑购买一个Mac或转换到Linux),以便轻松地修改serialVersionUID问题:
base_dir=$(pwd)
src_dir=$base_dir/src/main/java
ic_api_cp=$base_dir/target/classes
while read f
do
clazz=${f//\//.}
clazz=${clazz/%.java/}
seruidstr=$(serialver -classpath $ic_api_cp $clazz | cut -d ':' -f 2 | sed -e 's/^\s\+//')
perl -ni.bak -e "print $_; printf qq{%s\n}, q{ private $seruidstr} if /public class/" $src_dir/$f
done
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:
保存这个脚本,比如add_serialVersionUID。sh ~ / bin。然后在Maven或Gradle项目的根目录中运行它,比如:
add_serialVersionUID.sh < myJavaToAmend.lst
This .lst includes the list of java files to add the serialVersionUID in the following format:
这个.lst包含了以以下格式添加serialVersionUID的java文件列表:
com/abc/ic/api/model/domain/item/BizOrderTransDO.java
com/abc/ic/api/model/domain/item/CardPassFeature.java
com/abc/ic/api/model/domain/item/CategoryFeature.java
com/abc/ic/api/model/domain/item/GoodsFeature.java
com/abc/ic/api/model/domain/item/ItemFeature.java
com/abc/ic/api/model/domain/item/ItemPicUrls.java
com/abc/ic/api/model/domain/item/ItemSkuDO.java
com/abc/ic/api/model/domain/serve/ServeCategoryFeature.java
com/abc/ic/api/model/domain/serve/ServeFeature.java
com/abc/ic/api/model/param/depot/DepotItemDTO.java
com/abc/ic/api/model/param/depot/DepotItemQueryDTO.java
com/abc/ic/api/model/param/depot/InDepotDTO.java
com/abc/ic/api/model/param/depot/OutDepotDTO.java
This script uses the JDK serialVer tool under hood. So make sure your $JAVA_HOME/bin is in the PATH.
这个脚本使用了hood下的JDK serialVer工具。因此,确保您的$JAVA_HOME/bin在路径中。
#20
3
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 :
这个问题在《有效Java》中被Joshua Bloch很好地记录了下来。一本好书,一本必读的书。我将概述以下原因:
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.
序列化运行时为每个serializable类提供了一个名为串行版本的数字。这个数字叫做serialVersionUID。这个数字后面有一些数学运算,它基于类中定义的字段/方法。对于同一个类,每次都会生成相同的版本。在反序列化过程中使用这个数字来验证序列化对象的发送方和接收方已经为该对象加载了与序列化兼容的类。如果接收方已经为具有与相应发送方的类不同的serialVersionUID的对象加载了一个类,那么反序列化将导致无效classexception。
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.
如果类是可序列化的,您还可以显式声明自己的serialVersionUID,方法是声明一个名为“serialVersionUID”的字段,该字段必须是静态的、最终的、类型为long的。大多数IDE都像Eclipse一样帮助您生成长字符串。
#21
2
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.
每当对象被序列化时,对象的类都有一个版本ID号。这个ID称为serialVersionUID,它是基于关于类结构的信息进行计算的。假设您创建了一个Employee类,并且它具有版本id #333(由JVM分配),现在当您将序列化该类的对象(假设Employee对象)时,JVM将为它分配UID为#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.
考虑一种情况——在将来,您需要编辑或更改类,在这种情况下,当您修改它时,JVM将为它分配一个新的UID(假设#444)。现在,当您试图反序列化employee对象时,JVM会将序列化对象的(employee对象)version ID(#333)与类i进行比较。e# 444(因为它被更改了)。相比之下,JVM会发现两个版本的UID都不同,因此反序列化将失败。因此,如果每个类的serialVersionID是由程序员自己定义的。即使类是在将来发展的,它也是一样的,因此JVM总是会发现类与序列化的对象是兼容的,即使类是被修改的。有关更多信息,请参阅HEAD FIRST JAVA的第14章。