什么是serialVersionUID?serialVersionUID详解

2023-10-28 05:36
文章标签 详解 serialversionuid

本文主要是介绍什么是serialVersionUID?serialVersionUID详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

什么是serialVersionUID?serialVersionUID详解

概述

  • 所述的serialVersionUID属性是用来序列的标识符/反序列化的对象序列化类。

  • 序列化运行时与每个可序列化的类关联一个版本号,称为serialVersionUID,在反序列化期间使用该版本号来验证序列化对象的发送者和接收者是否已加载了该对象的与序列化兼容的类。

  • 如果接收者为对象加载的类serialVersionUID与相应的发送者的类不同,则反序列化将导致 InvalidClassException。可序列化的类可以serialVersionUID通过声明一个serialVersionUID必须为static,final和type的字段来显式声明其自身long:

  • private static final long serialVersionUID = 42L;
    
  • 如果可序列化的类未显式声明一个 serialVersionUID,则序列化运行时将根据serialVersionUID该类的各个方面为该类计算默认值,如Java对象序列化规范中所述。

  • 但是,强烈建议所有可序列化的类显式声明serialVersionUID值,因为默认serialVersionUID计算对类详细信息高度敏感,而类详细信息可能会根据编译器的实现而有所不同,因此可能在InvalidClassExceptions反序列化期间导致意外情况。

  • 因此,为了保证serialVersionUID不同Java编译器实现之间的值一致,可序列化的类必须声明一个显式serialVersionUID值。还强烈建议明确serialVersionUID声明尽可能使用private修饰符,因为此类声明仅适用于立即声明的类-serialVersionUID字段作为继承成员没有用。

序列号UID

  • 简而言之,我们使用serialVersionUID属性记住Serializable类的版本,以验证加载的类和序列化的对象是否兼容。

  • 不同类的serialVersionUID属性是独立的。因此,不同的类不必具有唯一的值。

  • 接下来,让我们通过一些示例来学习如何使用 serialVersionUID。

  • 首先创建一个可序列化的类,并声明一个serialVersionUID标识符:

  • public class AppleProduct implements Serializable {private static final long serialVersionUID = 1234567L;public String headphonePort;public String thunderboltPort;
    }
    
  • 接下来,我们将需要两个实用程序类:一个用于将AppleProduct对象序列化为String,另一个用于从该String反序列化该对象:

  • public class SerializationUtility {public static void main(String[] args) {AppleProduct macBook = new AppleProduct();macBook.headphonePort = "headphonePort2020";macBook.thunderboltPort = "thunderboltPort2020";String serializedObj = serializeObjectToString(macBook);System.out.println("Serialized AppleProduct object to string:");System.out.println(serializedObj);}public static String serializeObjectToString(Serializable o) {ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(o);oos.close();return Base64.getEncoder().encodeToString(baos.toByteArray());}
    }
    
  • public class DeserializationUtility {public static void main(String[] args) {String serializedObj = ... // ommited for claritySystem.out.println("Deserializing AppleProduct...");AppleProduct deserializedObj = (AppleProduct) deSerializeObjectFromString(serializedObj);System.out.println("Headphone port of AppleProduct:"+ deserializedObj.getHeadphonePort());System.out.println("Thunderbolt port of AppleProduct:"+ deserializedObj.getThunderboltPort());}public static Object deSerializeObjectFromString(String s)throws IOException, ClassNotFoundException {byte[] data = Base64.getDecoder().decode(s);ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));Object o = ois.readObject();ois.close();return o;}
    }
    
  • 我们从运行SerializationUtility.java开始,该程序将AppleProduct对象保存(序列化)为String实例,并使用Base64对字节进行编码

  • 然后,使用该String作为反序列化方法的参数,我们运行DeserializationUtility.java,该程序从给定的String重新组装(反序列化)AppleProduct对象。

  • 生成的输出应与此类似:

  • Serialized AppleProduct object to string:
    rO0ABXNyACljb20uYmFlbGR1bmcuZGVzZXJpYWxpemF0aW9uLkFwcGxlUHJvZHVjdAAAAAAAEta
    HAgADTAANaGVhZHBob25lUG9ydHQAEkxqYXZhL2xhbmcvU3RyaW5nO0wADmxpZ2h0ZW5pbmdQb3
    J0cQB+AAFMAA90aHVuZGVyYm9sdFBvcnRxAH4AAXhwdAARaGVhZHBob25lUG9ydDIwMjBwdAATd
    Gh1bmRlcmJvbHRQb3J0MjAyMA==
    
  • Deserializing AppleProduct...
    Headphone port of AppleProduct:headphonePort2020
    Thunderbolt port of AppleProduct:thunderboltPort2020
    
  • 现在,让我们 在AppleProduct.java中修改serialVersionUID常量,然后重新尝试从先前产生的同一String反序列化AppleProduct对象。重新运行DeserializationUtility.java应该生成此输出。

  • Deserializing AppleProduct...
    Exception in thread "main" java.io.InvalidClassException: com.baeldung.deserialization.AppleProduct; local class incompatible: stream classdesc serialVersionUID = 1234567, local class serialVersionUID = 7654321at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1630)at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1781)at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)at com.baeldung.deserialization.DeserializationUtility.deSerializeObjectFromString(DeserializationUtility.java:24)at com.baeldung.deserialization.DeserializationUtility.main(DeserializationUtility.java:15)
    
  • 通过更改类的serialVersionUID,我们修改了其版本/状态。结果,在反序列化期间未找到兼容的类,并且引发了InvalidClassException。

  • 如果Serializable类中未提供serialVersionUID,则JVM将自动生成一个。但是,优良作法是提供serialVersionUID值,并在更改类后对其进行更新,以便我们可以控制序列化/反序列化过程。我们将在下一部分中对其进行仔细研究。

兼容的变更

  • 假设我们需要在现有的AppleProduct类中添加一个新的lightningPort字段:

  • public class AppleProduct implements Serializable {
    //...public String lightningPort;
    }
    
  • 因为我们只是增加一个新的领域,在没有变化的serialVersionUID将需要。这是因为,在反序列化过程中,会将null分配为lightningPort字段的默认值。

  • 让我们修改DeserializationUtility类以打印此新字段的值:

  • System.out.println("LightningPort port of AppleProduct:"+ deserializedObj.getLightningPort());
    
  • 现在,当我们重新运行DeserializationUtility类时,我们将看到类似以下的输出:

  • Deserializing AppleProduct...
    Headphone port of AppleProduct:headphonePort2020
    Thunderbolt port of AppleProduct:thunderboltPort2020
    Lightning port of AppleProduct:null
    

默认串行版本

  • 如果我们不为Serializable 类定义 serialVersionUID 状态 ,则Java将根据类本身的某些属性(例如,类名,实例字段等)定义一个。

  • 让我们定义一个简单的 Serializable 类:

  • public class DefaultSerial implements Serializable {
    }
    
  • 如果我们像下面这样序列化此类的实例:

  • DefaultSerial instance = new DefaultSerial();
    System.out.println(SerializationUtility.serializeObjectToString(instance));
    
  • 这将打印序列化二进制文件的Base64摘要:

  • rO0ABXNyACpjb20uYmFlbGR1bmcuZGVzZXJpYWxpemF0aW9uLkRlZmF1bHRTZXJpYWx9iVz3Lz/mdAIAAHhw
    
  • 和以前一样,我们应该能够从摘要中反序列化此实例:

  • String digest = "rO0ABXNyACpjb20uYmFlbGR1bmcuZGVzZXJpY" + "WxpemF0aW9uLkRlZmF1bHRTZXJpYWx9iVz3Lz/mdAIAAHhw";
    DefaultSerial instance = (DefaultSerial) DeserializationUtility.deSerializeObjectFromString(digest);
    
  • 但是,对该类进行一些更改可能会破坏序列化兼容性。例如,如果我们向此类添加一个 私有 字段:

  • public class DefaultSerial implements Serializable {private String name;
    }
    
  • 然后尝试将相同的Base64摘要反序列化为类实例,我们将收到InvalidClassException:

  • Exception in thread "main" java.io.InvalidClassException: com.baeldung.deserialization.DefaultSerial; local class incompatible: stream classdesc serialVersionUID = 9045863543269746292, local class serialVersionUID = -2692722436255640434
    
  • 由于这种不必要的不兼容性,在Serializable类中声明serialVersionUID 始终是一个好主意。 这样,我们可以随着类本身的发展来保留或发展版本。

这篇关于什么是serialVersionUID?serialVersionUID详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/291032

相关文章

Python列表去重的4种核心方法与实战指南详解

《Python列表去重的4种核心方法与实战指南详解》在Python开发中,处理列表数据时经常需要去除重复元素,本文将详细介绍4种最实用的列表去重方法,有需要的小伙伴可以根据自己的需要进行选择... 目录方法1:集合(set)去重法(最快速)方法2:顺序遍历法(保持顺序)方法3:副本删除法(原地修改)方法4:

python logging模块详解及其日志定时清理方式

《pythonlogging模块详解及其日志定时清理方式》:本文主要介绍pythonlogging模块详解及其日志定时清理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录python logging模块及日志定时清理1.创建logger对象2.logging.basicCo

前端CSS Grid 布局示例详解

《前端CSSGrid布局示例详解》CSSGrid是一种二维布局系统,可以同时控制行和列,相比Flex(一维布局),更适合用在整体页面布局或复杂模块结构中,:本文主要介绍前端CSSGri... 目录css Grid 布局详解(通俗易懂版)一、概述二、基础概念三、创建 Grid 容器四、定义网格行和列五、设置行

Node.js 数据库 CRUD 项目示例详解(完美解决方案)

《Node.js数据库CRUD项目示例详解(完美解决方案)》:本文主要介绍Node.js数据库CRUD项目示例详解(完美解决方案),本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考... 目录项目结构1. 初始化项目2. 配置数据库连接 (config/db.js)3. 创建模型 (models/

SQL表间关联查询实例详解

《SQL表间关联查询实例详解》本文主要讲解SQL语句中常用的表间关联查询方式,包括:左连接(leftjoin)、右连接(rightjoin)、全连接(fulljoin)、内连接(innerjoin)、... 目录简介样例准备左外连接右外连接全外连接内连接交叉连接自然连接简介本文主要讲解SQL语句中常用的表

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

Python中局部变量和全局变量举例详解

《Python中局部变量和全局变量举例详解》:本文主要介绍如何通过一个简单的Python代码示例来解释命名空间和作用域的概念,它详细说明了内置名称、全局名称、局部名称以及它们之间的查找顺序,文中通... 目录引入例子拆解源码运行结果如下图代码解析 python3命名空间和作用域命名空间命名空间查找顺序命名空

SpringRetry重试机制之@Retryable注解与重试策略详解

《SpringRetry重试机制之@Retryable注解与重试策略详解》本文将详细介绍SpringRetry的重试机制,特别是@Retryable注解的使用及各种重试策略的配置,帮助开发者构建更加健... 目录引言一、SpringRetry基础知识二、启用SpringRetry三、@Retryable注解

springboot项目中常用的工具类和api详解

《springboot项目中常用的工具类和api详解》在SpringBoot项目中,开发者通常会依赖一些工具类和API来简化开发、提高效率,以下是一些常用的工具类及其典型应用场景,涵盖Spring原生... 目录1. Spring Framework 自带工具类(1) StringUtils(2) Coll

Python中的魔术方法__new__详解

《Python中的魔术方法__new__详解》:本文主要介绍Python中的魔术方法__new__的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、核心意义与机制1.1 构造过程原理1.2 与 __init__ 对比二、核心功能解析2.1 核心能力2.2