3266

serialVersionUID缺少 a 时 Eclipse 会发出警告。

可序列化类 Foo 未声明 long 类型的静态最终 serialVersionUID 字段

什么是serialVersionUID它以及为什么它很重要?请展示一个缺少serialVersionUID会导致问题的示例。

4

26 回答 26

2476

的文档java.io.Serializable可能与您得到的解释一样好:

序列化运行时将版本号与每个可序列化类相关联,称为 a serialVersionUID,在反序列化期间使用该版本号来验证序列化对象的发送方和接收方是否已为该对象加载了与序列化兼容的类。如果接收者为对象加载了一个serialVersionUID与相应发送者的类不同的类,则反序列化将导致 InvalidClassException. 可序列化的类可以通过声明一个必须是 static、final 和 typeserialVersionUID的字段来显式声明自己的:serialVersionUIDlong

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

如果可序列化类没有显式声明 a serialVersionUID,则序列化运行时将根据serialVersionUID类的各个方面为该类计算默认值,如 Java(TM) 对象序列化规范中所述。但是,强烈建议所有可序列化类显式声明serialVersionUID值,因为默认serialVersionUID计算对类细节高度敏感,这些细节可能因编译器实现而异,因此可能会InvalidClassExceptions在反序列化期间导致意外。因此,为了保证serialVersionUID不同 java 编译器实现的值一致,可序列化类必须声明一个显式serialVersionUID值。还强烈建议明确serialVersionUID声明尽可能使用 private 修饰符,因为此类声明仅适用于立即声明的类——serialVersionUID字段作为继承成员没有用。

于 2008-11-12T23:30:15.710 回答
501

如果您只是因为为了实现而必须序列化而进行序列化(谁在乎您是否为HTTPSession.de-serializing忽略这个。

如果您实际上正在使用序列化,那么只有当您计划直接使用序列化存储和检索对象时才重要。代表您的serialVersionUID课程版本,如果您的课程的当前版本与其以前的版本不向后兼容,您应该增加它。

大多数时候,您可能不会直接使用序列化。如果是这种情况,请通过单击快速修复选项生成默认值SerialVersionUID,不要担心。

于 2008-11-13T04:24:57.923 回答
338

我不能错过这个插入 Josh Bloch 的书Effective Java (2nd Edition) 的机会。第 10 章是 Java 序列化不可或缺的资源。

根据 Josh,自动生成的 UID 是根据类名、实现的接口以及所有公共和受保护成员生成的。以任何方式更改其中任何一项都会更改serialVersionUID. 因此,只有当您确定不会超过一个版本的类被序列化(跨进程或稍后从存储中检索)时,您才需要弄乱它们。

如果你暂时忽略它们,然后发现你需要以某种方式更改类但要保持与旧版本的类的兼容性,你可以使用 JDK 工具serialverserialVersionUID类上生成,并显式设置在新课上。(根据您的更改,您可能还需要通过添加writeObjectreadObject方法来实现自定义序列化 - 请参阅Serializablejavadoc 或前面提到的第 10 章。)

于 2008-11-12T23:37:18.830 回答
142

您可以告诉 Eclipse 忽略这些 serialVersionUID 警告:

窗口 > 首选项 > Java > 编译器 > 错误/警告 > 潜在的编程问题

如果您不知道,您可以在本节中启用许多其他警告(甚至将一些警告报告为错误),其中许多非常有用:

  • 潜在的编程问题:可能的意外布尔赋值
  • 潜在的编程问题:空指针访问
  • 不必要的代码:从不读取局部变量
  • 不必要的代码:冗余空检查
  • 不必要的代码:不必要的强制转换或“instanceof”

还有很多。

于 2008-11-13T01:17:00.953 回答
122

serialVersionUID便于序列化数据的版本控制。它的值在序列化时与数据一起存储。反序列化时,会检查相同的版本以查看序列化数据与当前代码的匹配情况。

如果要对数据进行版本化,通常从serialVersionUID0 开始,并随着类的每次结构更改而改变序列化数据(添加或删除非瞬态字段)。

内置的反序列化机制 ( in.defaultReadObject()) 将拒绝从旧版本的数据中反序列化。但是如果你愿意,你可以定义你自己的readObject()函数,它可以读回旧数据。然后,此自定义代码可以检查serialVersionUID以了解数据所在的版本并决定如何对其进行反序列化。如果您存储在多个版本的代码中存在的序列化数据,则此版本控制技术很有用。

但是将序列化数据存储这么长的时间跨度并不常见。使用序列化机制将数据临时写入缓存或通过网络将其发送到具有相同版本代码库相关部分的另一个程序更为常见。

在这种情况下,您对保持向后兼容性不感兴趣。您只关心确保正在通信的代码库确实具有相同版本的相关类。为了便于进行此类检查,您必须保持与serialVersionUID以前一样,并且在更改类时不要忘记对其进行更新。

如果您确实忘记更新该字段,您最终可能会得到一个具有不同结构但具有相同serialVersionUID. 如果发生这种情况,默认机制 ( in.defaultReadObject()) 将不会检测到任何差异,并尝试反序列化不兼容的数据。现在您可能会遇到一个神秘的运行时错误或静默失败(空字段)。这些类型的错误可能很难找到。

因此,为了帮助这个用例,Java 平台为您提供了不serialVersionUID手动设置的选择。相反,类结构的哈希将在编译时生成并用作 id。这种机制将确保您永远不会有具有相同 id 的不同类结构,因此您不会遇到上面提到的这些难以跟踪的运行时序列化故障。

但是自动生成的 id 策略有一个缺点。也就是说,为同一类生成的 id 可能在编译器之间有所不同(如上面 Jon Skeet 所述)。因此,如果您在使用不同编译器编译的代码之间传递序列化数据,建议还是手动维护 id。

如果您像提到的第一个用例那样向后兼容您的数据,您可能还想自己维护 id。这是为了获得可读的 id 并更好地控制它们何时以及如何更改。

于 2012-10-03T06:05:45.110 回答
79

什么是serialVersionUID,我为什么要使用它?

SerialVersionUID是每个类的唯一标识符,JVM使用它来比较类的版本,确保在反序列化期间加载序列化期间使用的相同类。

指定一个可以提供更多控制,但如果您不指定,JVM 会生成一个。生成的值在不同的编译器之间可能不同。此外,有时您出于某种原因只想禁止对旧的序列化对象 [ backward incompatibility] 进行反序列化,在这种情况下,您只需更改 serialVersionUID。

javadocsSerializable

默认的 serialVersionUID 计算对类细节高度敏感,这些细节可能因编译器实现而异,因此可能InvalidClassException在反序列化期间导致意外的 s。

因此,您必须声明 serialVersionUID,因为它给了我们更多的控制权

这篇文章有一些关于这个话题的好观点。

于 2013-10-17T04:28:23.880 回答
63

最初的问题是询问“为什么它很重要”和“示例”,这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
}
于 2013-04-07T10:43:27.890 回答
54

首先我需要解释一下什么是序列化。

序列化允许将对象转换为流,以便通过网络发送该对象或保存到文件或保存到数据库中以供字母使用。

序列化有一些规则

  • 一个对象只有在其类或其超类实现了 Serializable 接口时才可序列化

  • 一个对象是可序列化的(它本身实现了 Serializable 接口),即使它的超类不是。然而,可序列化类的层次结构中的第一个超类,没有实现可序列化接口,必须有一个无参数的构造函数。如果违反,readObject() 将在运行时产生 java.io.InvalidClassException

  • 所有原始类型都是可序列化的。

  • 瞬态字段(带有瞬态修饰符)未序列化(即未保存或恢复)。实现 Serializable 的类必须标记不支持序列化的类的瞬态字段(例如,文件流)。

  • 静态字段(带有 static 修饰符)未序列化。

Object被序列化时,Java 运行时关联序列版本号,即serialVersionID.

我们需要serialVersionID的地方:

在反序列化期间验证发送方和接收方在序列化方面是否兼容。如果接收器加载了不同的类,serialVersionID则反序列化将以InvalidClassCastException. 可序列化的类可以通过声明一个名为的字段来显式
声明自己,该字段必须是静态的、最终的和长类型的。serialVersionUIDserialVersionUID

让我们用一个例子来试试这个。

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)
于 2018-01-22T04:27:40.490 回答
41

如果您永远不需要将对象序列化为字节数组并发送/存储它们,那么您无需担心。如果这样做,那么您必须考虑您的 serialVersionUID,因为对象的反序列化器会将其与其类加载器所具有的对象版本相匹配。在 Java 语言规范中阅读有关它的更多信息。

于 2008-11-12T23:30:30.233 回答
35

如果您在从未考虑过序列化的类上收到此警告,并且您没有声明自己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 方法。我认为在这种情况下不应该有警告(因为您通常不能可靠地序列化和反序列化此类匿名类,无论如何跨类的不同版本),但我不确定编译器如何识别这一点。

于 2011-02-03T00:32:03.213 回答
22

要了解字段 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。

于 2013-06-02T06:20:31.203 回答
20

至于缺少 serialVersionUID 可能导致问题的示例:

我正在开发这个由使用EJB模块的 Web 模块组成的 Java EE 应用程序。Web 模块EJB远程调用该模块并传递一个作为参数POJO实现的。Serializable

这个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
于 2013-05-20T19:32:37.583 回答
17

不要打扰,默认计算非常好,足以满足 99,9999% 的情况。如果您遇到问题,您可以 - 如前所述 - 在需要时引入 UID(这是极不可能的)

于 2012-04-29T15:59:41.077 回答
16

我通常serialVersionUID在一个上下文中使用:当我知道它将离开 Java VM 的上下文时。

当我使用我的应用程序或者我知道我使用的库/框架将使用它时ObjectInputStream,我会知道这一点。ObjectOutputStreamserialVersionID 确保不同版本或供应商的不同 Java VM 将正确互操作,或者如果它在 VM 外部存储和检索,例如HttpSession即使在应用程序服务器重新启动和升级期间会话数据也可以保留。

对于所有其他情况,我使用

@SuppressWarnings("serial")

因为大多数时候默认serialVersionUID就足够了。这包括Exception, HttpServlet

于 2014-03-04T16:17:48.520 回答
14

字段数据表示存储在类中的一些信息。类实现了Serializable接口,所以eclipse自动提供声明serialVersionUID字段。让我们从那里设置的值 1 开始。

如果您不希望出现该警告,请使用以下命令:

@SuppressWarnings("serial")
于 2013-03-13T14:26:53.937 回答
12

SerialVersionUID 用于对象的版本控制。您也可以在类文件中指定 serialVersionUID。不指定 serialVersionUID 的后果是,当您在类中添加或修改任何字段时,已经序列化的类将无法恢复,因为为新类和旧序列化对象生成的 serialVersionUID 将不同。Java 序列化过程依赖于正确的 serialVersionUID 来恢复序列化对象的状态,并在 serialVersionUID 不匹配的情况下抛出 java.io.InvalidClassException

阅读更多:http: //javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html#ixzz3VQxnpOPZ

于 2015-03-25T21:08:11.297 回答
12

为什么在 Java 中使用SerialVersionUID内部类?Serializable

在 期间serialization,Java 运行时会为一个类创建一个版本号,以便以后可以对其进行反序列化。SerialVersionUID这个版本号在 Java中被称为。

SerialVersionUID用于版本化序列化数据。如果类SerialVersionUID与序列化实例匹配,则只能反序列化。当我们不在SerialVersionUID类中声明时,Java 运行时会为我们生成它,但不建议这样做。建议声明SerialVersionUIDprivate static final long变量以避免默认机制。

当您Serializable通过实现标记接口声明一个类java.io.Serializable时,Java 运行时使用默认的序列化机制将该类的实例持久保存到磁盘中,前提是您没有使用Externalizable接口自定义进程。

另请参阅为什么在 Java 中的 Serializable 类中使用 SerialVersionUID

于 2017-03-07T05:48:02.947 回答
11

如果 CheckStyle 可以验证实现 Serializable 的类上的 serialVersionUID 是否具有良好的值,那就太好了,即它与串行版本 id 生成器将产生的相匹配。例如,如果您有一个具有大量可序列化 DTO 的项目,请记住删除现有的 serialVersionUID 并重新生成它是一件痛苦的事情,而目前(据我所知)验证这一点的唯一方法是为每个类重新生成并比较旧的。这是非常非常痛苦的。

于 2008-12-03T14:44:33.713 回答
8

如果您想修改大量没有设置 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 中。

于 2015-06-27T12:36:07.687 回答
6

Joshua Bloch 在 Effective Java 中很好地记录了这个问题。一本非常好的书,必须阅读。我将概述以下一些原因:

序列化运行时为每个可序列化类提供一个称为序列版本的数字。这个数字称为serialVersionUID。现在这个数字后面有一些数学,它是根据类中定义的字段/方法得出的。对于同一个类,每次都会生成相同的版本。在反序列化期间使用此数字来验证序列化对象的发送方和接收方是否已加载该对象的与序列化兼容的类。如果接收者为对象加载了一个类,该对象的 serialVersionUID 与相应发送者的类不同,则反序列化将导致 InvalidClassException。

如果该类是可序列化的,您还可以通过声明一个名为“serialVersionUID”的字段来显式声明您自己的serialVersionUID,该字段必须是静态的、最终的且类型为long。像 Eclipse 这样的大多数 IDE 可以帮助您生成那个长字符串。

于 2015-08-03T19:41:31.070 回答
6

每次对对象进行序列化时,该对象都会被标记为对象类的版本 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 章。

于 2016-04-17T19:42:31.100 回答
3

一个简单的解释:

  1. 你在序列化数据吗?

    序列化基本上是将类数据写入文件/流/等。反序列化是将数据读回类。

  2. 你打算投入生产吗?

    如果您只是用不重要/假数据测试某些东西,那么不要担心(除非您直接测试序列化)。

  3. 这是第一个版本吗?

    如果是这样,设置serialVersionUID=1L.

  4. 这是第二个、第三个等产品版本吗?

    现在您需要担心serialVersionUID,并且应该深入研究它。

基本上,如果在更新需要写入/读取的类时没有正确更新版本,则在尝试读取旧数据时会出错。

于 2019-01-16T23:29:01.183 回答
3

'serialVersionUID' 是一个 64 位数字,用于在反序列化过程中唯一标识一个类。当您序列化一个对象时,该类的serialVersionUID 也会写入文件。每当您反序列化此对象时,java 运行时都会从序列化数据中提取此 serialVersionUID 值,并比较与该类关联的相同值。如果两者都不匹配,则会抛出“java.io.InvalidClassException”。

如果可序列化类没有显式声明 serialVersionUID,则序列化运行时将根据类的各个方面(如字段、方法等)计算该类的 serialVersionUID 值,您可以参考此链接以获取演示应用程序。

于 2020-01-20T15:14:59.203 回答
2

首先回答你的问题,当我们没有在我们的类中声明 SerialVersionUID 时,Java 运行时会为我们生成它,但是该过程对许多类元数据很敏感,包括字段数、字段类型、字段的访问修饰符、实现的接口按类等。因此建议自己声明它,Eclipse 也会警告你。

序列化:我们经常使用状态(对象变量中的数据)非常重要的重要对象,以至于在将对象状态发送给其他人的情况下,我们不能冒因电源/系统故障(或)网络故障而丢失它的风险机器。这个问题的解决方案被命名为“持久性”,它仅仅意味着持久化(保存/保存)数据。序列化是实现持久性的许多其他方法之一(通过将数据保存到磁盘/内存)。保存对象的状态时,重要的是为对象创建一个标识,以便能够正确地读回它(反序列化)。这个唯一标识是 ID 是 SerialVersionUID。

于 2019-04-29T05:06:22.230 回答
1

长话短说,该字段用于检查序列化数据是否可以正确反序列化。序列化和反序列化通常由程序的不同副本进行 - 例如服务器将对象转换为字符串,客户端将接收到的字符串转换为对象。该字段表明两者都以关于该对象是什么的相同想法进行操作。此字段在以下情况下有帮助:

  • 您在不同的地方有许多不同的程序副本(例如 1 个服务器和 100 个客户端)。如果您要更改对象,更改版本号并忘记更新此客户端,它将知道他无法反序列化

  • 您已将数据存储在某个文件中,稍后您尝试使用已修改对象的程序的更新版本打开它 - 如果您保持版本正确,您将知道该文件不兼容

什么时候重要?

最明显 - 如果您向对象添加一些字段,旧版本将无法使用它们,因为它们的对象结构中没有这些字段。

不太明显 - 当您反序列化对象时,字符串中不存在的字段将保留为 NULL。如果您从对象中删除了字段,旧版本会将此字段保留为 allways-NULL,如果旧版本依赖此字段中的数据,这可能会导致行为不端(无论如何,您创建它是为了某事,而不仅仅是为了好玩 :-))

最不明显 - 有时你会改变你在某些领域的含义的想法。例如,当您 12 岁时,您的意思是“自行车”下的“自行车”,但当您 18 岁时,您的意思是“摩托车”——如果您的朋友邀请您“骑自行车穿越城市”,那么您将是唯一一个骑自行车,你会明白跨领域保持相同含义的重要性:-)

于 2019-04-15T16:55:03.613 回答
0

什么是 SerialVersionUID?回答: - 假设有两个人,一个来自总部,另一个来自 ODC,他们都将分别执行序列化和反序列化。在这种情况下,为了验证 ODC 中的接收者是经过身份验证的人,JVM 创建了一个唯一 ID,称为 SerialVersionUID。

这是基于场景的一个很好的解释,

为什么选择 SerialVersionUID?

序列化:在序列化的时候,每个对象发送方JVM都会保存一个唯一标识符。JVM 负责根据发送方系统中存在的相应 .class 文件生成该唯一 ID。

反序列化:在反序列化时,接收方 JVM 会将与对象关联的唯一 ID 与本地类唯一 ID 进行比较,即 JVM 还将根据接收方系统中存在的相应 .class 文件创建一个唯一 ID。如果两个唯一 ID 匹配,则仅执行反序列化。否则我们会得到运行时异常,说 InvalidClassException。这个唯一标识符不过是 SerialVersionUID

于 2019-11-15T16:06:05.990 回答