Java序列化与反序列化

Java开发时,有时需要实现序列化和反序列化操作。这里记录下序列化与反序列化的使用总结。

定义

序列化是将Java对象转换为字节序列的过程。在序列化过程中,Java对象被转换为一个字节流。
反序列化是将字节序列转换回Java对象的过程。在反序列化过程中,字节序列被读取并转换回原始的Java对象。
请添加图片描述

常见使用场景

在梳理序列化与反序列化的应用场景前,需要明确的一点是,序列化和反序列化针对的都是对象,不是类。以下两个场景是序列化与反序列化常用的场景:
(1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中,也即持久化对象;
(2) 在网络上传送对象的字节序列,也即网络传输对象;
此外,有时为提供访问性能,会将对象存放到缓存中。

使用示例

在Java中,如果一个对象要想实现序列化,必须要实现 Serializable 接口或 Externalizable 接口。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类采用默认的序列化方式。
序列化和反序列化的三方件很多,常用的有阿里巴巴的fastjson、Spring默认的序列化组件 jackson等。基于fastjson或jackson的实现,还请自行学习使用。这里使用JDK自带的ObjectOutputStream和ObjectInputStream为例来实现,其中:
ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
接下来举例说明两个接口的实现。

基于 Serializable 接口实现序列化

基于 Serializable 接口实现序列化是实现序列化的常用方式,必须掌握。接下来介绍下使用示例。

// (1) 定义实体并实现Serializable接口
public class User implements Serializable {// 通过IDEA等编辑器自动生成serialVersionUIDprivate static final long serialVersionUID = 5355682299368213966L;private String username;private transient String password;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}
}
// (2) 序列化和反序列化上述实体类
public class TransientDemo {public void testTransientByStream() throws IOException, ClassNotFoundException {User user = new User();user.setUsername("foo");user.setPassword("123456789");ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(user); // 序列化oos.close();System.out.println(bos.toByteArray());ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);User deserializedUser = (User) ois.readObject(); // 反序列化ois.close();System.out.println(deserializedUser.getUsername()); // 输出 "foo"System.out.println(deserializedUser.getPassword()); // 输出 null,因为 password 属性没有被序列化}
}

在基于 Serializable 接口实现序列化时,如无特殊说明,均需显式地定义serialVersionUID。顾名思义,serialVersionUID就是实体类的版本号。一般情况下,该值一旦设置后,均不需要调整,以保证软件版本的兼容性。需要说明的是,如果没有显式定义serialVersionUID,Java编辑器会自动生成serialVersionUID,但是还是推荐显式定义serialVersionUID。这是因为serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。此外,serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的 serialVersionUID。为了提高serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显式的定义serialVersionUID并为其赋值。
此外,对于不期望序列化的字段,可以使用transient关键字修饰,更多transient关键字的使用,可以参考transient关键字使用说明一文。主流的序列化组件(如fastjson、jackson等)均支持基于 Serializable 接口实现序列化。

基于 Externalizable 接口实现序列化

Externalizable接口是Serializable接口的子类,开发者需要自行实现writeExternal()和readExternal()方法,用来决定哪些字段需要进行序列化和反序列化。注意,transient关键字在该场景下不再生效。
此外,需要说明的是,对Externalizable对象反序列化时,会先调用类的无参构造方法,这是有别于默认反序列方式的。如果把类的不带参数的构造方法删除,或者把该构造方法的访问权限设置为private、默认或protected级别,会抛出java.io.InvalidException: no valid constructor的异常。因此Externalizable对象必须有默认构造函数,而且必须是public的。

// (1) 定义实体并实现Serializable接口
public class Person implements Externalizable {private transient String name;private int age;// 必须定义访问权限是public的无参构造函数public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return this.name;}public int getAge() {return this.age;}// 必须实现writeExternal接口@Overridepublic void writeExternal(ObjectOutput out) throws IOException {out.writeUTF(name);out.writeInt(age);}// 必须实现readExternal接口@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {name = in.readUTF();age = in.readInt();}
}
// (2) 序列化和反序列化上述实体类
public class TransientDemo {public void testExternalWithTransient() throws IOException, ClassNotFoundException {Person person = new Person("jack", 25);ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(person);oos.close();System.out.println(bos.toByteArray());ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);Person deserializedPerson = (Person) ois.readObject();ois.close();System.out.println(deserializedPerson.getName()); // 输出 "foo",即使已经使用transient关键字修饰了字段,System.out.println(deserializedPerson.getAge()); // 输出 25}
}

优缺点分析

Java序列化和反序列化的优点是它们提供了一种在不同平台之间传输和共享Java对象的方法。但是,Java序列化和反序列化的缺点是它们可能会破坏对象的封装性,因为序列化操作会将对象的所有字段都暴露给外部。在使用Java序列化和反序列化时,需要注意以下几点:
(1) 优先选择基于Serializable接口实现序列化:相比基于 Externalizable 接口实现序列化,主流序列化框架均支持基于Serializable接口的序列化。
(2) 在基于 Serializable 接口实现序列化时,需显式地定义serialVersionUID。serialVersionUID是实体类的版本号,用来实现版本控制。为了保证兼容性,强烈建议在一个可序列化类中显式的定义serialVersionUID并为其赋值。
(3) 对于不期望序列化的字段,可以使用transient关键字修饰。transient关键字可以用来标记某个字段不需要被序列化。如果一个对象包含一个使用了transient关键字的字段,那么该字段将无法序列化。
(4) 安全性问题:Java序列化和反序列化可能会暴露关键信息,因此需要确保安全性。对于一些关键信息,如果需要在网络间传输(以非安全协议传输时),需要使用加密来保护序列化的数据。

参考

https://www.zhihu.com/tardis/bd/ans/672095170?source_id=1001 java中什么是序列化和反序列化?
https://blog.csdn.net/qq_44543508/article/details/103232007 java中的transient关键字详解
https://www.baidu.com/ 百度AI搜索
https://www.cnblogs.com/lqmblog/p/8530108.html 序列化和反序列化的简单理解
https://www.cnblogs.com/huhx/p/5303024.html java基础---->Serializable的使用
https://www.cnblogs.com/huhx/p/sSerializableTheory.html java高级---->Serializable的过程分析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/126819.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV(二十九):图像腐蚀

1.图像腐蚀原理 腐蚀操作的原理是将一个结构元素(也称为核或模板)在图像上滑动,并将其与图像中对应位置的像素进行比较。如果结构元素的所有像素与图像中对应位置的像素都匹配,那么该位置的像素值保持不变。如果结构元素的任何一个…

freemarker模板引擎详解以及使用方法

哈喽!大家好,我是旷世奇才李先生 文章持续更新,可以微信搜索【小奇JAVA面试】第一时间阅读,回复【资料】更有我为大家准备的福利哟,回复【项目】获取我为大家准备的项目 文章目录 一、freemarker 介绍1、简介 二、free…

Llama 2 论文《Llama 2: Open Foundation and Fine-Tuned Chat Models》阅读笔记

文章目录 Llama 2: Open Foundation and Fine-Tuned Chat Models1.简介2.预训练2.1 预训练数据2.2 训练详情2.3 LLAMA 2 预训练模型评估 3. 微调3.1 supervised Fine-Tuning(SFT)3.2 Reinforcement Learning with Human Feedback (RLHF)3.2.1 人类偏好数据收集3.2.2 奖励模型训…

Excel VSTO开发11-自定义菜单项

版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。 11 自定义菜单项 自定义菜单项可以在插件启动时候添加,即增加到ThisAddIn_Startup() 内。 下面以具体代码说明&#x…

Mysql锁

文章目录 1. 概述2. 分类3. 全局锁4. 表级锁5. 行级锁 1. 概述 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并…

Leetcode:349. 两个数组的交集【题解超详细】

题目 给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 难度:简单 题目链接:349.两个数组的交集 示例 1: 输入:nums1 [1,2,2,1], nums2 [2,…

2023国赛 C题论文 蔬菜类商品自动定价与补货策略

因为一些不可抗力,下面仅展示小部分论文,其余看文末 一、问题重述 在生鲜超市管理领域,涉及一系列复杂问题,包括供应链管理、定价策略以及市场需求分析等方面。以蔬菜类商品为案例,这些商品在生鲜商超中具有较短的保…

开源电商项目 Mall:构建高效电商系统的终极选择

文章目录 Mall 项目概览前台商城系统后台管理系统系统架构图业务架构图 模块介绍后台管理系统 mall-admin商品管理:功能结构图-商品订单管理:功能结构图-订单促销管理:功能结构图-促销内容管理:功能结构图-内容用户管理&#xff1…

python串口采集数据绘制波形图

这个示例使用 matplotlib 绘制图形,它能够从串口实时读取数据并绘制成波形图。确保你已经替换了 ‘COM11’ 和 9600 为正确的串口号和波特率。 import serial import matplotlib.pyplot as plt from collections import deque import struct# 配置串口参数 ser s…

SQL SERVER 如何实现UNDO REDO 和PostgreSQL 有近亲关系吗

开头还是介绍一下群,如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,SQL Server,Redis ,Oracle ,Oceanbase 等有问题,有需求都可以加群群内有各大数据库行业大咖,CTO,可以解决你的问题。加群请加微信号 l…

【C++入门】C语言的不足之处

概要 C入门主要讲的是C语言的一些不足,C作为补充,来补充C的不足之处 C的关键字有63个,C语言有32个(作为了解,不需要专门记) 变量的命名规则: 变量名必须以字母或下划线开头。变量名只能包含字…

【C#项目实战】控制台游戏勇士斗恶龙(1)——游戏初始设置以及开始界面

君兮_的个人主页 即使走的再远,也勿忘启程时的初心 C/C 游戏开发 Hello,米娜桑们,这里是君兮_,最近开始正式的步入学习游戏开发的正轨,想要通过写博客的方式来分享自己学到的知识和经验,这就是开设本专栏的目的。希望…

jenkins 发布job切换不同的jdk版本/ maven版本

1. 技术要求 因为有个新的项目需要使用jdk17 而旧的项目需要jdk1.8 这就需要jenkins在发布项目的时候可以指定jdk版本 2. 解决 jenkins全局工具配置页面 配置新的jdk 路径 系统管理-> 全局工具配置 如上新增个jdk 名称叫 jdk-17 然后配置jdk-17的根路径即可(这…

抓安卓日志命令

临时生成logcat文件 ctrl z 是停止 adb logcat -> /home/log/log.txt 导出全部日志 adb root adb remount 只抓安卓日志 logcat (所有保存的日志,中途关机和开机都有) adb pull log/android D:\73log\1android 抓全部日志&#x…

八大排序——快速排序

Hello,大家好,今天分享的八大排序里的快速排序,所谓快速排序是一个叫霍尔的人发明,有很多人可能会觉得为什么不叫霍尔排序,其中原因就是因为它快,快速则体现了它的特点,今天我们就来讲一下快速排…

YOLOV7改进-具有隐式知识学习的Efficient解耦头

[解耦头][https://github.com/z1069614715/objectdetection_script/blob/master/yolo-improve/yolov7-DecoupledHead.py] 1、复制这些到yolo.py 2、到这 3、复制下半部分到yolo.py 4、替换这里 5、最后的加到上面的这里 6、添加 7、添加 8、V5大概一个点的提升 9、解…

OpenLdap +PhpLdapAdmin + Grafana docker-compose部署安装

目录 一、OpenLdap介绍 二、PhpLdapAdmin介绍 三、使用docker-compose进行安装 1. docker-compose.yml 2. grafana配置文件 3. provisioning 四、安装openldap、phpldapadmin、grafana 五、配置OpenLDAP 1. 登陆PhpLdapAdmin web管理 2. 需要注意的细节 内容介绍参考…

【GO语言基础】控制流

系列文章目录 【Go语言学习】ide安装与配置 【GO语言基础】前言 【GO语言基础】变量常量 【GO语言基础】数据类型 【GO语言基础】控制流 文章目录 系列文章目录条件语句if-else 结构判断一个字符串是否为空:switch结构 循环结构for 循环(C风格&#xff…

ref 操作 React 定时器

秒表 需要将 interval ID 保存在 ref 中&#xff0c;以便在需要时能够清除计时器。 import { useRef, useState } from "react";const SecondWatch () > {const [startTime, setStartTime] useState<any>(null);const [now, setNow] useState<any>…

Vue+elementUI 导出word打印

import JSZipUtils from "jszip-utils"; import JSZip from "pizzip"; import Docxtemplater from "docxtemplater"; npm安装以上依赖 首先维护个word模板 导出方法 //导出wordskipOutWord(row) {var printData rowconst data JSON.parse(JS…