【问题标题】:What means 1L serialVersionUID? When could I use this default value 1L?1L serialVersionUID 是什么意思?我什么时候可以使用这个默认值 1L?
【发布时间】:2014-02-18 10:29:06
【问题描述】:

有 3 种方式来定义 serialVersionUID :

1. private static final long serialVersionUID = 1L; (Default)
2. private static final long serialVersionUID = -8940196742313994740L; (Generated)
3. Don't define serialVersionUID and let the JVM define it at runtime. @Lance Java

但我不明白第一种方式!

我已经看到,有人在源代码中为所有 java 类定义了“serialVersionUID=1L”。

什么意思?有用吗?

如果所有类都有相同的serialVersionUID 1L,没有问题吗?

【问题讨论】:

  • 实际值无关紧要,重要的是,当对类进行不兼容的更改时,您会更改它。看这里:stackoverflow.com/questions/3284979/…
  • serialVersionUID 分配给什么通常无关紧要。
  • @Rhys 这个说法确实太笼统了。
  • 好问题。任何其他语言使用这样的功能?
  • 注意,还有第三种选择。不要定义serialVersionUID,让JVM在运行时定义。

标签: java


【解决方案1】:

什么意思?有用吗?

是的。 serialVersionUID 的目的是让程序员控制哪些版本的类在序列化方面被认为是不兼容的。只要 serialVersionUID 保持不变,序列化机制就会尽最大努力翻译序列化实例,这可能不是您想要的。如果您进行的语义更改导致旧版本不兼容,您可以更改 serialVersionUID 以使旧实例反序列化失败。

如果所有类都有相同的serialVersionUID 1L,没有问题吗?

否 - serialVersionUID 是每个类

【讨论】:

    【解决方案2】:

    这是解释here

    serialVersionUID 是 Serializable 类的通用版本标识符。反序列化使用这个数字来确保加载的类与序列化的对象完全对应。如果未找到匹配项,则抛出 InvalidClassException。


    来自javadoc

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


    有用的链接

    1. java.io.Serializable
    2. Why should I bother about serialVersionUID? (StackOverflow)
    3. serialVersionUID in Java Serialization

    【讨论】:

    • 这个明显引用的来源?
    【解决方案3】:

    如果序列化对象的serialVersionUID 与反序列化对象的类的serialVersionUID 不匹配,JVM 将抛出InvalidClassException

    每次类以不兼容的方式更改时,类的serialVersionUID 都应该更改。通常这意味着每次更改类的shape(即字段或方法更改)。

    在某些情况下,您不希望 serialVersionUID 更改。例如,您可能会在应用程序中接受旧版本的对象。在这种情况下,您可以保持 serialVersionUID 不变,新字段将作为 null 出现。

    【讨论】:

      【解决方案4】:

      是的,我也见过这样定义serialVersionUID 的代码。我认为这是个坏主意。

      在这种情况下,serialVersionUID 字段只有一个“可区分”值;即零 ...0L。零表示“根据您正在序列化/反序列化的实际类,通过应用标准算法计算版本 id 在运行时”。这意味着每当您的代码的有效序列化签名发生变化时,序列化/反序列化代码将使用不同的版本 ID。从(大图)类型安全的角度来看,这是最安全的做法,尽管它也有些低效,而且可能更脆弱。

      什么意思?

      1L 没有特殊含义。它只是一个将1L 匹配为“其他”版本ID 的数字。

      在我看来,您最好使用0L,或者使用标准算法(在某些时候)生成的版本号。

      • 如果您使用0L,那么如果类以可能导致问题的方式发生变化,您就会得到明确的反序列化异常。如果您需要这个,这是一件好事。

      • 另一方面,您使用生成的版本 ID,您(程序员)可以自行决定何时重新生成该 ID。当你决定重新生成时,只有当类签名 改变时,id 才会改变。 (如果类表示等没有改变,重新生成的签名应该和原来的签名一样!)当id 确实改变时,你可以考虑是否添加自定义方法('readObject',等)来处理不兼容问题。

      • 但是,如果您使用 1L,如果不检查您的代码历史记录并比较旧/新版本的类,您将无法判断版本 id 是否需要更改...回到你需要的地方。

      有用吗?

      这取决于您认为“有用”的含义。如果您认为将版本 ID 硬连接到“相信我,没问题”是件好事,那么1L 很有用。


      我的回忆是,某些版本的 Eclipse 提供 1L 作为对缺少的 serialVersionUID 字段警告的可能自动更正之一。这很可能是您看到的实例的来源。

      【讨论】:

      • 不是说通过指定一个数字类似于版本控制提交吗?
      • 谁的争执?不是OP的。不是我的。而且每次提交时更改数字也没有用。如果这就是你的意思.....
      【解决方案5】:

      假设你用serialVersionUID 编写一个类,实例化它,然后将它序列化到一个文件(使用 ObjectOutputStream)

      然后你修改类。

      然后,使用修改后的类,反序列化(读入)修改前序列化的版本。 Java会检查当前类和序列化类的serialVersionUID,如果不匹配,反序列化就会失败。这是一种故意的快速失败,以防止以后由于类版本不兼容而发生更微妙(更难调试)的错误。

      如果您省略serialVersionUID,则禁用版本检查。 如果您始终将 if 设置为 1L,则检查将始终通过(值始终相同),因此您仍然容易受到细微的类版本不兼容问题的影响。

      【讨论】:

      • 将其设置为 1L 不会禁用版本检查。
      • 如果你总是设置为1L,那么它会,因为serialVersionUID总是一样的,所以检查总是会通过的。
      【解决方案6】:

      在序列化对象并将对象反序列化回 JVM 时使用该值。

      此外,如果您的类发生更改并且您不想支持向后兼容性(即能够将使用您的上一个版本的类序列化的对象反序列化),您可以将版本号更改为任何其他值。

      但是,为了支持向后兼容性,您需要保持与之前设置的 serialVersionUID 值相同的版本号。

      最佳做法是每次对类进行一些不兼容的更改时更改 serialVersionUID。

      【讨论】:

      • 为什么这是最佳做法?哪里说的?
      【解决方案7】:

      重要的是要明确这样一个事实,即让类实现 Serializable 接口会使 ALL 字段未声明 transient 成为类的导出 API 的一部分,无论这些字段是否声明为private

      换句话说,实现Serializable:

      • 破坏封装。如果这个类有机会成为一个成功的、长寿的类,那么你必须支持序列化形式……永远。

      • 可能会严重影响您发展该类的能力,正是因为它是其导出 API 的一部分。另一种方法是破坏向后兼容性。

      • 可能会为您的类及其应用程序带来安全问题。反序列化代表了一种在没有构造函数的情况下生成 Java 对象的方法,因此通过向反序列化工具提供流氓字节流可能会违反类的不变量。

      serialVerionUID 应该被认为是序列化形式的一个属性。它旨在向一个 JVM 传达它正在接收的类实例的序列化形式与在其他地方呈现(可能)的同一类的序列化形式之间是否存在差异。

      如果序列化的表单不同但 UID 相同,您可以看到可能出现的潜在问题。接收 JVM 将假定接收到的旧类和新类之间的串行表单版本是相同的,而实际上它们不是相同的,并且会尽职尽责地尝试反序列化字节流。

      TLDR:您不应该随意更改 UID。当类的序列化形式发生变化时,您应该更改它,以便使用旧版本的类(具有不同的序列化形式)的软件版本会中断,而不是(可能默默地)做错事.如果你的类没有设计一个好的序列化形式,将使其更难(甚至更难)为其客户提供向后兼容性。在理想情况下,一个类的序列化形式会在其整个演化过程中持续存在(因此它的 UID 永远不需要改变)。

      【讨论】:

        【解决方案8】:

        您可以将任何long 值分配给serialVersionUID,但每次修改类时都必须更改它。

        第二个看起来像一个生成的serialVersionUID,基于当前类版本的特性。

        【讨论】:

        • 哪里说每次修改类都得改!
        • 我不知道它在哪里说的,但是尝试反序列化使用不同版本的类序列化的对象可能会导致令人惊讶的行为。分配不同的serialVersionUID 将直接导致异常。没有更多令人讨厌的惊喜......
        • 并非每次都必须更改它,但如果不更改,则需要确定该类的新版本是否仍与旧的序列化版本兼容。做出这样的决定并非易事。每次更换更安全。
        猜你喜欢
        • 2010-10-27
        • 2017-05-03
        • 2011-10-24
        • 2018-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-23
        相关资源
        最近更新 更多