缺少 serialVersionUID
时 Eclipse 会发出警告。
可序列化类 Foo 未声明 long 类型的静态最终 serialVersionUID 字段
什么是 serialVersionUID
,为什么它很重要?请显示缺少 serialVersionUID
会导致问题的示例。
java.io.Serializable
的文档可能与您得到的解释一样好:
序列化运行时与每个可序列化类关联一个版本号,称为 serialVersionUID,在反序列化期间使用该版本号来验证序列化对象的发送方和接收方是否已加载与序列化兼容的该对象的类。如果接收者为对象加载了一个类,该对象的 serialVersionUID 与相应发送者的类不同,则反序列化将导致 InvalidClassException。可序列化的类可以通过声明一个名为 serialVersionUID 的字段来显式声明它自己的 serialVersionUID,该字段必须是静态的、最终的和 long 类型:
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
如果可序列化类没有显式声明 serialVersionUID,则序列化运行时将根据类的各个方面为该类计算默认的 serialVersionUID 值,如 Java(TM) 对象序列化规范中所述。但是,强烈建议所有可序列化的类都显式声明 serialVersionUID 值,因为默认的 serialVersionUID 计算对类细节高度敏感,这些细节可能因编译器实现而异,因此可能在反序列化期间导致意外的 InvalidClassExceptions。因此,为了保证在不同的 java 编译器实现之间具有一致的 serialVersionUID 值,可序列化的类必须声明一个显式的 serialVersionUID 值。还强烈建议显式 serialVersionUID 声明尽可能使用 private 修饰符,因为此类声明仅适用于立即声明的类 - serialVersionUID 字段作为继承成员没有用。
如果您只是因为为了实现而必须序列化而进行序列化(例如,谁在乎您是否为 HTTPSession
序列化......如果它被存储了,您可能不关心 de-serializing
表单对象),那么你可以忽略它。
如果您实际上正在使用序列化,那么只有当您计划直接使用序列化存储和检索对象时才重要。 serialVersionUID
代表您的课程版本,如果您的课程的当前版本与其以前的版本不向后兼容,您应该增加它。
大多数时候,您可能不会直接使用序列化。如果是这种情况,请通过单击快速修复选项生成默认 SerialVersionUID
,不要担心。
serialVersionUID
保护您免受不兼容更改的观点是有效的。如果您不希望将类用于永久存储,则使用 @SuppressWarnings
可以更好地记录意图。
serialVersionUID
是最后的手段,是绝望的建议。
我不能错过这个插入 Josh Bloch 的书 Effective Java(第 2 版)的机会。第 10 章是 Java 序列化不可或缺的资源。
根据 Josh,自动生成的 UID 是根据类名、实现的接口以及所有公共和受保护成员生成的。以任何方式更改其中任何一项都会更改 serialVersionUID
。因此,只有当您确定不会超过一个版本的类被序列化(跨进程或稍后从存储中检索)时,您才需要弄乱它们。
如果您暂时忽略它们,稍后发现您需要以某种方式更改类但要保持与旧版本类的兼容性,您可以使用 JDK 工具 serialver 生成 {1 } 在 old 类上,并在新类上显式设置。 (根据您的更改,您可能还需要通过添加 writeObject
和 readObject
方法来实现自定义序列化 - 请参阅 Serializable
javadoc 或上述第 10 章。)
您可以告诉 Eclipse 忽略这些 serialVersionUID 警告:
窗口 > 首选项 > Java > 编译器 > 错误/警告 > 潜在的编程问题
如果您不知道,您可以在本节中启用许多其他警告(甚至将一些警告报告为错误),其中许多非常有用:
潜在的编程问题:可能的意外布尔赋值
潜在的编程问题:空指针访问
不必要的代码:从不读取局部变量
不必要的代码:冗余空检查
不必要的代码:不必要的强制转换或“instanceof”
还有很多。
serialVersionUID
有助于对序列化数据进行版本控制。它的值在序列化时与数据一起存储。反序列化时,会检查相同的版本以查看序列化数据与当前代码的匹配情况。
如果您想对数据进行版本化,通常从 serialVersionUID
的 0 开始,并在每次更改序列化数据(添加或删除非瞬态字段)的类的结构更改时对其进行调整。
内置的反序列化机制 (in.defaultReadObject()
) 将拒绝从旧版本的数据中反序列化。但是,如果您愿意,您可以定义自己的 readObject()-函数,它可以读回旧数据。然后,此自定义代码可以检查 serialVersionUID
以了解数据所在的版本并决定如何对其进行反序列化。如果您存储在多个版本的代码中存在的序列化数据,则此版本控制技术很有用。
但是将序列化数据存储这么长的时间跨度并不常见。使用序列化机制将数据临时写入缓存或通过网络将其发送到具有相同版本代码库相关部分的另一个程序更为常见。
在这种情况下,您对保持向后兼容性不感兴趣。您只关心确保正在通信的代码库确实具有相同版本的相关类。为了便于进行此类检查,您必须像以前一样维护 serialVersionUID
,并且在更改类时不要忘记更新它。
如果您确实忘记更新该字段,您最终可能会得到一个具有不同结构但具有相同 serialVersionUID
的两个不同版本的类。如果发生这种情况,默认机制 (in.defaultReadObject()
) 将不会检测到任何差异,并尝试反序列化不兼容的数据。现在您可能会遇到一个神秘的运行时错误或静默失败(空字段)。这些类型的错误可能很难找到。
因此,为了帮助这个用例,Java 平台为您提供了不手动设置 serialVersionUID
的选择。相反,类结构的哈希将在编译时生成并用作 id。这种机制将确保您永远不会有具有相同 id 的不同类结构,因此您不会遇到上面提到的这些难以跟踪的运行时序列化故障。
但是自动生成的 id 策略有一个缺点。也就是说,为同一类生成的 id 可能在编译器之间有所不同(如上面 Jon Skeet 所述)。因此,如果您在使用不同编译器编译的代码之间传递序列化数据,建议还是手动维护 id。
如果您像提到的第一个用例那样向后兼容您的数据,您可能还想自己维护 id。这是为了获得可读的 id 并更好地控制它们何时以及如何更改。
什么是 serialVersionUID,我为什么要使用它?
SerialVersionUID
是每个类的唯一标识符,JVM
使用它来比较类的版本,确保在反序列化期间加载序列化期间使用的相同类。
指定一个可以提供更多控制,但如果您不指定,JVM 会生成一个。生成的值在不同的编译器之间可能不同。此外,有时您出于某种原因只想禁止对旧的序列化对象 [backward incompatibility
] 进行反序列化,在这种情况下,您只需更改 serialVersionUID。
默认的 serialVersionUID 计算对类细节高度敏感,这些细节可能因编译器实现而异,因此可能在反序列化期间导致意外的 InvalidClassExceptions。
因此,您必须声明 serialVersionUID,因为它给了我们更多的控制权。
This article 在这个主题上有一些很好的观点。
serialVersionUID
导致意想不到的后果。汤姆安德森对 MetroidFan2002 的回答的评论解决了这个问题:“我想说,如果你不使用序列化来永久存储,你应该使用 @SuppressWarnings 而不是添加一个值。它减少了类的混乱,它保留了serialVersionUID 机制保护您免受不兼容的更改。”
serialVersionUID
不是“每个类的唯一标识符”。完全限定的类名就是这样。它是一个版本指标。
原始问题询问了“为什么它很重要”和“示例”,此 Serial Version ID
将在哪些地方有用。好吧,我找到了一个。
假设您创建了一个 Car
类,将其实例化,然后将其写入对象流。扁平化的汽车对象在文件系统中存在一段时间。同时,如果通过添加新字段来修改Car
类。稍后,当您尝试读取(即反序列化)扁平化的 Car
对象时,您会得到 java.io.InvalidClassException
——因为所有可序列化的类都会自动获得一个唯一标识符。当类的标识符不等于展平对象的标识符时,将引发此异常。如果你真的想一想,抛出异常是因为添加了新字段。您可以通过声明显式的 serialVersionUID 自行控制版本控制来避免引发此异常。显式声明 serialVersionUID
也有一点性能优势(因为不必计算)。因此,最佳实践是在您创建 Serializable 类后立即将您自己的 serialVersionUID 添加到您的 Serializable 类中,如下所示:
public class Car {
static final long serialVersionUID = 1L; //assign a long value
}
1
递增的自然数等不冲突。
serialVersionUID
与 'find[ing] the right version of the class' 没有任何关系。
首先我需要解释一下什么是序列化。
序列化允许将对象转换为流,以便通过网络发送该对象或保存到文件或保存到数据库中以供字母使用。
序列化有一些规则。
一个对象只有在其类或其超类实现了 Serializable 接口时才可序列化
一个对象是可序列化的(它本身实现了 Serializable 接口),即使它的超类不是。然而,可序列化类的层次结构中的第一个超类,没有实现可序列化接口,必须有一个无参数的构造函数。如果违反,readObject() 将在运行时产生 java.io.InvalidClassException
所有原始类型都是可序列化的。
瞬态字段(带有瞬态修饰符)未序列化(即未保存或恢复)。实现 Serializable 的类必须标记不支持序列化的类的瞬态字段(例如,文件流)。
静态字段(带有 static 修饰符)未序列化。
当 Object
被序列化时,Java 运行时会关联序列版本号,即 serialVersionID
。
我们需要serialVersionID的地方:
在反序列化期间验证发送方和接收方在序列化方面是否兼容。如果接收者使用不同的 serialVersionID
加载类,则反序列化将以 InvalidClassCastException
结束。
可序列化类可以通过声明一个名为 serialVersionUID
的字段来显式声明自己的 serialVersionUID
,该字段必须是静态的、最终的、和长的类型。
让我们用一个例子来试试这个。
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() {
return getEmpName() + " is " + getEmpAge() + "years old";
}
}
创建序列化对象
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");
}
}
反序列化对象
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());
}
}
注意:现在更改 Employee 类的 serialVersionUID 并保存:
private static final long serialVersionUID = 4L;
并执行 Reader 类。不执行 Writer 类,你会得到异常。
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)
如果您永远不需要将对象序列化为字节数组并发送/存储它们,那么您无需担心。如果这样做,那么您必须考虑您的 serialVersionUID,因为对象的反序列化器会将其与其类加载器所具有的对象版本相匹配。在 Java 语言规范中阅读有关它的更多信息。
如果您在从未考虑过序列化的类上收到此警告,并且您没有声明自己 implements Serializable
,这通常是因为您从实现 Serializable 的超类继承。通常,委托给这样的对象而不是使用继承会更好。
所以,而不是
public class MyExample extends ArrayList<String> {
public MyExample() {
super();
}
...
}
做
public class MyExample {
private List<String> myList;
public MyExample() {
this.myList = new ArrayList<String>();
}
...
}
并在相关方法中调用 myList.foo()
而不是 this.foo()
(或 super.foo()
)。 (这并不适用于所有情况,但仍然很常见。)
我经常看到人们扩展 JFrame 之类的东西,而他们真的只需要委托这个。 (这也有助于在 IDE 中自动完成,因为 JFrame 有数百种方法,当您想在类上调用自定义方法时不需要这些方法。)
警告(或serialVersionUID)不可避免的一种情况是当您从AbstractAction 扩展时,通常在匿名类中,只添加actionPerformed 方法。我认为在这种情况下不应该有警告(因为您通常不能可靠地序列化和反序列化此类匿名类,无论如何跨类的不同版本),但我不确定编译器如何识别这一点。
__AUTOLOAD
的功能,我不知道。
要了解字段 serialVersionUID 的意义,应该了解序列化/反序列化的工作原理。
当 Serializable 类对象被序列化时,Java 运行时将序列版本号(称为 serialVersionUID)与该序列化对象相关联。在您反序列化此序列化对象时,Java Runtime 将序列化对象的 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。反过来反序列化也不会成功。因此,为了避免这种情况/问题,程序员必须始终指定 Serializable 类的 serialVersionUID。
至于缺少 serialVersionUID 可能导致问题的示例:
我正在开发这个由使用 EJB
模块的 Web 模块组成的 Java EE 应用程序。 Web 模块远程调用 EJB
模块并传递实现 Serializable
作为参数的 POJO
。
这个 POJO's
类被打包在 EJB jar 和它自己的 jar 中,位于 Web 模块的 WEB-INF/lib 中。它们实际上是同一个类,但是当我打包 EJB 模块时,我解压了这个 POJO 的 jar 以将它与 EJB 模块打包在一起。
对 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
不要打扰,默认计算非常好,足以满足 99,9999% 的情况。如果您遇到问题,您可以 - 如前所述 - 在需要时引入 UID(这是极不可能的)
我通常在一个上下文中使用 serialVersionUID
:当我知道它将离开 Java VM 的上下文时。
当我将 ObjectInputStream
和 ObjectOutputStream
用于我的应用程序时,或者如果我知道我使用的库/框架将使用它时,我会知道这一点。 serialVersionID 确保不同版本或供应商的不同 Java VM 将正确互操作,或者如果它在 VM 外部存储和检索,例如 HttpSession
,即使在应用程序服务器重新启动和升级期间,会话数据也可以保留。
对于所有其他情况,我使用
@SuppressWarnings("serial")
因为大多数时候默认的 serialVersionUID
就足够了。这包括 Exception
、HttpServlet
。
字段数据表示存储在类中的一些信息。类实现 Serializable
接口,因此 eclipse 自动提供声明 serialVersionUID
字段。让我们从那里设置的值 1 开始。
如果您不希望出现该警告,请使用以下命令:
@SuppressWarnings("serial")
为什么在 Java 的 Serializable
类中使用 SerialVersionUID
?
在 serialization
期间,Java 运行时会为类创建一个版本号,以便稍后对其进行反序列化。此版本号在 Java 中称为 SerialVersionUID
。
SerialVersionUID
用于对序列化数据进行版本化。如果一个类的 SerialVersionUID
与序列化实例匹配,则您只能反序列化它。当我们不在类中声明 SerialVersionUID
时,Java 运行时会为我们生成它,但不推荐这样做。建议将 SerialVersionUID
声明为 private static final long
变量以避免默认机制。
当您通过实现标记接口 java.io.Serializable
将类声明为 Serializable
时,Java 运行时会使用默认的序列化机制将该类的实例保存到磁盘中,前提是您尚未使用 Externalizable
接口自定义进程。
另见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
如果 CheckStyle 可以验证实现 Serializable 的类上的 serialVersionUID 是否具有良好的值,那就太好了,即它与串行版本 id 生成器将产生的相匹配。例如,如果您有一个包含许多可序列化 DTO 的项目,请记住删除现有的 serialVersionUID 并重新生成它是一件痛苦的事情,目前(据我所知)验证这一点的唯一方法是为每个类重新生成并比较旧的。这是非常非常痛苦的。
serialver
将产生的内容。 -1
serialVersionUID
设置为 1。如果类的较新版本不兼容,但仍需要能够处理旧数据,则增加版本号并添加处理旧格式的特殊代码。每次看到大于 1 位的 serialVersionUID
时我都会哭,要么是因为它是一个随机数(没用),要么是因为该类显然需要处理 10 多个不同的版本。
如果您想修改大量没有设置 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
你保存这个脚本,对你说 add_serialVersionUID.sh ~/bin。然后在 Maven 或 Gradle 项目的根目录中运行它,例如:
add_serialVersionUID.sh < myJavaToAmend.lst
此 .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
此脚本在后台使用 JDK serialVer 工具。因此,请确保您的 $JAVA_HOME/bin 在 PATH 中。
Joshua Bloch 在 Effective Java 中很好地记录了这个问题。一本非常好的书,必须阅读。我将概述以下一些原因:
序列化运行时为每个可序列化类提供一个称为序列版本的数字。这个数字称为serialVersionUID。现在这个数字后面有一些数学,它是根据类中定义的字段/方法得出的。对于同一个类,每次都会生成相同的版本。在反序列化期间使用此编号来验证序列化对象的发送方和接收方是否已为该对象加载了与序列化兼容的类。如果接收者为对象加载了一个类,该对象的 serialVersionUID 与相应发送者的类不同,则反序列化将导致 InvalidClassException。
如果该类是可序列化的,您还可以通过声明一个名为“serialVersionUID”的字段来显式声明您自己的serialVersionUID,该字段必须是静态的、最终的并且类型为long。像 Eclipse 这样的大多数 IDE 可以帮助您生成那个长字符串。
每次对对象进行序列化时,该对象都会被标记为对象类的版本 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 章。
serialVersionUID
。它不是随每个对象一起发送的。
一个简单的解释:
你在序列化数据吗?序列化基本上是将类数据写入文件/流/等。反序列化是将数据读回类。你打算投入生产吗?如果您只是用不重要/假数据测试某些东西,那么不要担心(除非您直接测试序列化)。这是第一个版本吗?如果是这样,设置 serialVersionUID=1L。这是第二个、第三个等产品版本吗?现在您需要担心 serialVersionUID,并且应该深入研究它。
基本上,如果在更新需要写入/读取的类时没有正确更新版本,则在尝试读取旧数据时会出错。
'serialVersionUID' 是一个 64 位数字,用于在反序列化过程中唯一标识一个类。当您序列化一个对象时,该类的serialVersionUID 也会写入文件。每当您反序列化此对象时,java 运行时都会从序列化数据中提取此 serialVersionUID 值,并比较与该类关联的相同值。如果两者都不匹配,则会抛出“java.io.InvalidClassException”。
如果可序列化类没有显式声明 serialVersionUID,则序列化运行时将根据类的各个方面(如字段、方法等)计算该类的 serialVersionUID 值,您可以参考此 link 进行演示应用程序。
首先回答你的问题,当我们没有在我们的类中声明 SerialVersionUID 时,Java 运行时会为我们生成它,但是该过程对许多类元数据很敏感,包括字段数、字段类型、字段的访问修饰符、实现的接口按类等。因此建议自己声明它,Eclipse 也会警告你。
序列化:我们经常使用状态(对象变量中的数据)非常重要的重要对象,以至于在将对象状态发送给其他人的情况下,我们不能冒因电源/系统故障(或)网络故障而丢失它的风险机器。这个问题的解决方案被命名为“持久性”,它仅仅意味着持久化(保持/保存)数据。序列化是实现持久性的许多其他方法之一(通过将数据保存到磁盘/内存)。保存对象的状态时,重要的是为对象创建一个标识,以便能够正确地读回它(反序列化)。这个唯一标识是 ID 是 SerialVersionUID。
长话短说,该字段用于检查序列化数据是否可以正确反序列化。序列化和反序列化通常由程序的不同副本进行 - 例如服务器将对象转换为字符串,客户端将接收到的字符串转换为对象。该字段表明两者都以关于该对象是什么的相同想法进行操作。此字段在以下情况下有帮助:
您在不同的地方有许多不同的程序副本(例如 1 个服务器和 100 个客户端)。如果您要更改对象,更改版本号并忘记更新此客户端,它将知道他无法反序列化
您已将数据存储在某个文件中,稍后您尝试使用已修改对象的程序的更新版本打开它 - 如果您保持版本正确,您将知道该文件不兼容
什么时候重要?
最明显 - 如果您向对象添加一些字段,旧版本将无法使用它们,因为它们的对象结构中没有这些字段。
不太明显 - 当您反序列化对象时,字符串中不存在的字段将保留为 NULL。如果您从对象中删除了字段,旧版本会将此字段保留为 allways-NULL,如果旧版本依赖此字段中的数据,这可能会导致行为不端(无论如何,您创建它是为了某事,而不仅仅是为了好玩 :-))
最不明显 - 有时你会改变你在某些领域的含义的想法。例如,当您 12 岁时,您的意思是“自行车”下的“自行车”,但当您 18 岁时,您的意思是“摩托车”——如果您的朋友邀请您“骑自行车穿越城市”,那么您将是唯一一个骑自行车,你会明白跨领域保持相同含义的重要性:-)
不定期副业成功案例分享