Java中的序列化与反序列化(一)
1、概述
大家好,我是欧阳方超。今天来看一下Java序列化与反序列化的问题。
2、序列化与反序列化
2.1、序列化与反序列化的概念
在Java中,序列化是将对象转换为可存储或传输的格式(一般为字节流)的过程,序列化后的字节流可以被传输给远程系统,并在那里重新构造成原始对象。反序列化则是将序列化的数据转换回原始对象的过程,反序列化是对象序列化的逆过程,通过反序列化操作能够在接收端恢复出与发送端相同的对象。这是Java中的一种非常重要的机制,可以将对象转换为字节流或其他格式,以便在网络上传输或在磁盘上存储,并在需要的时候进行恢复。
2.2、serialVersionUID出现的缘由
如果在序列化和反序列化前后,类的结构发生了变化(属性有增删),会导致反序列化失败。
为了避免这种情况,Java中引入了serialVersionUID这个机制,用于标识序列化版本。当一个对象被序列化时,会将这个对象的类的serialVersionUID的值写入到字节流中,当一个对象被反序列化时,JVM会从字节流中读取serialVersionUID的值,并将其与反序列化对象所对应类的serialVersionUID值进行比较。如果这两个值不一致,就会抛出InvalidClassException异常,表示反序列化失败。我们用一个例子来验证一下:
import java.io.*;public class TestMain {public static void main(String[] args) {Person person = new Person("Jhon", 20);try {FileOutputStream fileOutputStream = new FileOutputStream("D:\\home\\person.ser");ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);objectOutputStream.writeObject(person);objectOutputStream.close();System.out.println("Serialized data is saved in person.ser");}catch (IOException e) {e.printStackTrace();}}
}class Person implements Serializable {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}
}
上面的例子中,创建了Person类的对象,并经过序列化后保存到了磁盘上,文件名为person.ser,接下来我们为Person类额外添加一个属性address,并试图从磁盘上的person.ser文件恢复出Person类的对象,
import java.io.*;public class TestMain {public static void main(String[] args) {Person person = null;//从文件中反序列化对象try {FileInputStream fileInputStream = new FileInputStream("D:\\home\\person.ser");ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);person = (Person)objectInputStream.readObject();System.out.println(person.getAge());System.out.println(person.getName());} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}
}class Person implements Serializable {private String name;private int age;private String address;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}public String getAddress() {return address;}
}
此时是会报错的,
java.io.InvalidClassException: com.Person; local class incompatible: stream classdesc serialVersionUID = -9177105843612582313, local class serialVersionUID = 6433972014701326216at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885)at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
报错的原因从上面的错误日志也可以看出,是由于“stream classdesc serialVersionUID”与“local class serialVersionUID”导致的错误 。
为了避免这种情况,Java中引入了serialVersionUID这个机制,用于标识序列化版本。当一个对象被序列化时,会将这个对象的类的serialVersionUID的值写入到字节流中,当一个对象被反序列化时,JVM会从字节流中读取serialVersionUID的值,并将其与反序列化对象所对应类的serialVersionUID值进行比较。如果这两个值不一致,就会抛出InvalidClassException异常,表示反序列化失败。
如果一个类没有显式地定义serialVersionUID,则在序列化时会根据类的结构自动生成一个,但是这个自动生成的serialVersionUID值是不可控的。如果类的结构发生了变化,可能会导致自动生成的serialVersionUID与旧版本不一致,从而导致反序列化失败。为了避免这种情况,我们应该显式地定义serialVersionUID,以确保它的唯一性,并在类结构发生变化时手动更新它,以确保类的可序列化和反序列化的兼容性。
下面的示例中,为Person类指定了serialVersionUID,其值为1L,注意,通常我们使用随机数生成器或者手工定义一个值,这样将Person类序列化到本地person.ser文件后,不论怎么为Person类增删属性(待验证的name和age属性就别删了),总能恢复出创建Person对象时为name和age指定的值。
import java.io.*;public class TestMain {public static void main(String[] args) {Person person = new Person("Jhon", 20);try {FileOutputStream fileOutputStream = new FileOutputStream("D:\\home\\person.ser");ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);objectOutputStream.writeObject(person);objectOutputStream.close();System.out.println("Serialized data is saved in person.ser");}catch (IOException e) {e.printStackTrace();}Person person1 = null;//从文件中反序列化对象try {FileInputStream fileInputStream = new FileInputStream("D:\\home\\person.ser");ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);person1 = (Person)objectInputStream.readObject();System.out.println(person1.getAge());System.out.println(person1.getName());} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}// 验证反序列化后的对象与原始对象是否一致System.out.println("Name: " + person1.getName());System.out.println("Age: " + person1.getAge());}
}class Person implements Serializable {private static final long serialVersionUID = 1L;private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}
}
3、总结
所以,为了避免反序列失败,为序列化类新增属性时,建议不要修改 serialVersionUID 字段的值,当然如果完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。关于序列化和反序列化还有很多其他内容,我们择日继续。
我是欧阳方超,把事情做好了自然就有兴趣了,如果你喜欢我的文章,欢迎点赞、转发、评论加关注。下回见。
相关文章:
Java中的序列化与反序列化(一)
1、概述 大家好,我是欧阳方超。今天来看一下Java序列化与反序列化的问题。 2、序列化与反序列化 2.1、序列化与反序列化的概念 在Java中,序列化是将对象转换为可存储或传输的格式(一般为字节流)的过程,序列化后的字…...
3.函数、结构体、包
一、函数定义和调用 package mainimport ("fmt" )func test() {fmt.Println("hello world") } func main() {test() }二、函数的参数 1.单个参数 func test(n int) {fmt.Println("传递进来的参数是", n) } func main() {test(10) }2.多个参数…...
科普丨关于 A/B 测试的十问十答
你想知道的,都在这里!本文是神策数据「十问十答」科普系列文章的第二期,围绕 A/B 测试展开。 1 Q:什么是 A/B 测试? A:A/B 测试作为互联网企业的核心增长手段之一,其价值已在实际应用中被多次验…...
尚融宝——整合OpenFeign与Sentinel实现兜底方法——验证手机号码是否注册功能
一、整合过程 在项目添加依赖:添加位置 <!--服务调用--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency> 在需要的服务中添加启动注…...
几种常见的激活函数
文章目录 常见的激活函数介绍Sigmoid函数ReLU函数LeakyReLU函数Tanh函数Softmax函数总结 常见的激活函数介绍 激活函数是神经网络中的重要组成部分,它决定了神经元的输出。在神经网络的前向传播中,输入数据被传递给神经元,经过加权和和激活函…...
MySQL-数据库,数据表的基本操作
数据库,数据表的基本操作 1 数据库的基本操作1.1 创建数据库1.2 删除数据库 2 数据表的基本操作2.1 创建数据表2.1.1 主键约束2.1.2 外键约束2.1.3 非空约束2.1.4 唯一性约束2.1.5 默认约束2.1.6 自增列 2.2 查看数据表结构2.2.1 查看表基本结构语句DESC2.2.2 查看表…...
IC-14W网络IC卡读写器_银河麒麟桌面操作系统V10适配测试报告
银河麒麟操作系统产品NeoCertify 认证测试报告 系统版本:银河麒麟桌面操作系统V10 厂商名称: 广州荣士电子有限公司 认证产品:IC-14W网络IC卡读写器 测试日期: 2022-11-04 …...
面试常见问题
1.自我介绍 面试官您好,我叫**,在招聘网站上看到了公司的介绍和岗位要求,觉得非常有吸引力,结合自己的过往经历,也感觉自己能够胜任,所以很高兴能有这次面试机会。 参与工作已经5年了,18年毕业于…...
matlab数据归一化与反归一化处理
假如数据实际取值范围为 X i ∈ [ − π π ] , i 1 , 2 , 3 X_i \in [-\pi \ \ \pi], i1,2,3 Xi∈[−π π],i1,2,3,变量服从正态分布 示例如下: %% 数据归一化处理及其概率密度函数 clear clc Mu [0 0 0]; % 均值 Sigma [1 1 1]; % 标准差 C…...
【杂凑算法篇】密码杂凑算法的安全强度
【杂凑算法篇】密码杂凑算法的安全强度 杂凑(哈希)算法安全强度—【蘇小沐】 文章目录 【杂凑算法篇】密码杂凑算法的安全强度(一)安全强度(Security Strength)(二)杂凑算法的安全强度与对比总…...
【RobotFramework自动化测试】
robotframework介绍 RF是一个基于Python语言开发的,可扩展的,是以关键字驱动模式的自动化测试框架。RF最新的版本是2019 年7月份发布,7月份之前只支持python2.7,7月之后支持3.X的版本 robotframework的安装 安装python环境&…...
操作系统原理 —— 什么是中断?(四)
我们先来看看早期的计算机的工作流程: 如上图,在早期的计算机假设有三个程序需要执行,执行顺序是:先执行程序1,等待程序1结束之后,再开始执行程序2,以此类推,所以它们是串行执行的…...
SA168 3BSE003389R1
SA168 3BSE003389R1 远程终端控制系统(RTU)可连接到其他设备。RTU可将设备上的电气信号转换为数字的值,例如一个开关或阀开/关的状态,或是仪器量测到的压力、流量、电压或电流。也可以借由信号转换及传送信号来控制设备࿰…...
基于Java+Springboot+Vue+elememt美食论坛平台设计实现
基于JavaSpringbootVueelememt美食论坛平台设计实现 博主介绍:5年java开发经验,专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文…...
Go Etcd
基本操作 go get go.etcd.io/etcd/client/v3# 此处使用的 版本是: # go.etcd.io/etcd/client/v3 v3.5.8这里使用的是 "go.etcd.io/etcd/client/v3" 而不是 "go.etcd.io/etcd/clientv3" 我们不使用 etcd/clientv3,因为它与grpc 最新…...
01、Cadence使用记录之新建工程与基础操作(原理图绘制:OrCAD Capture CIS)
01、Cadence使用记录之新建工程与基础操作(原理图绘制:OrCAD Capture CIS) 硕士学电磁场去了,写点博客记录下学习过程。 参考的教程是B站的视频:allegro软件入门视频教程全集100讲 本科的时候就对Cadence有所耳闻&am…...
Redis数据结构与对象-链表和字典
1、链表 其实个人感觉redis的链表内容和其他的差不多。就是一个listNode结构,里面又指向前置节点和后置节点的指针。 然后redis链表可以保存各种不同类型的值。 链表被广泛用于实现redis的各种功能,比如列表键、发布与订阅、慢查询、监视器等。 2、字典…...
学系统集成项目管理工程师(中项)系列08a_合同管理(上)
1. 合同(Contract) 1.1. 契约 1.2. 广义概念 1.2.1. 以确定各种权利与义务为内容的协议,即只要是当事人之间达成的确定权利义务的协议均为合同,不管它涉及哪个法律部门及何种法律关系 1.2.2. 合同除应包括民法中的合同外&…...
【Linux 裸机篇(四)】I.MX6ULL C语言 LED 驱动
文章目录 一、汇编搭建 C 语言环境二、C 语言编写三、编写 Makefile四、链接脚本 一、汇编搭建 C 语言环境 实际工作中是很少用到汇编去写嵌入式驱动的,大部分情况下都是使用 C 语言去编写的。只是在开始部分用汇编来初始化一下 C 语言环境,比如初始化 D…...
我也曾经因安装库而抓狂,直到我遇到了
入门教程、案例源码、学习资料、读者群 请访问: python666.cn 大家好,欢迎来到 Crossin的编程教室 ! 几乎所有的 Python 学习者都遇到过“安装”方面的问题。这些安装问题包括 Python 自身环境的安装、第三方模块的安装、不同版本的切换&…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...
【JavaSE】绘图与事件入门学习笔记
-Java绘图坐标体系 坐标体系-介绍 坐标原点位于左上角,以像素为单位。 在Java坐标系中,第一个是x坐标,表示当前位置为水平方向,距离坐标原点x个像素;第二个是y坐标,表示当前位置为垂直方向,距离坐标原点y个像素。 坐标体系-像素 …...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...
