Java-IO流之序列化与反序列化详解
Java-IO流之序列化与反序列化详解
- 一、序列化与反序列化概述
- 1.1 基本概念
- 1.2 核心接口与类
- 1.3 应用场景
- 二、Java序列化的基本实现
- 2.1 实现Serializable接口
- 2.2 使用ObjectOutputStream进行序列化
- 2.3 使用ObjectInputStream进行反序列化
- 三、序列化的高级特性
- 3.1 serialVersionUID的作用
- 3.2 瞬态关键字transient
- 3.3 自定义序列化方法
- 3.4 实现Externalizable接口
- 四、序列化的最佳实践
- 4.1 始终声明serialVersionUID
- 4.2 处理敏感字段
- 4.3 实现readObjectNoData方法
- 4.4 序列化单例和枚举
- 4.5 序列化集合和数组
- 五、序列化的常见问题与解决方案
- 5.1 NotSerializableException
- 5.2 InvalidClassException
- 5.3 性能问题
- 5.4 安全风险
- 六、替代序列化方案
- 6.1 JSON序列化
- 6.2 Protobuf序列化
- 6.3 Kryo序列化
Java中对象的序列化(Serialization)与反序列化(Deserialization)是一项重要技术,为对象的持久化和远程传输提供了基础支持,它允许将对象转换为字节流以便存储或传输,也可以将字节流还原为原始对象。这项技术在分布式系统、远程方法调用(RMI)、缓存机制等场景中有着广泛的应用。本文我将深入探讨Java序列化与反序列化的原理、实现方法及最佳实践,带你全面掌握这一核心技术。
一、序列化与反序列化概述
1.1 基本概念
- 序列化(Serialization):将Java对象转换为字节流的过程
- 反序列化(Deserialization):将字节流恢复为Java对象的过程
1.2 核心接口与类
- Serializable接口:标记接口,实现该接口的类可以被序列化
- Externalizable接口:继承自Serializable,提供更细粒度的序列化控制
- ObjectOutputStream:用于将对象写入输出流
- ObjectInputStream:用于从输入流读取对象
1.3 应用场景
- 对象持久化:将对象保存到文件或数据库
- 远程通信:在网络中传输对象
- 缓存机制:将对象缓存到内存或磁盘
- 分布式系统:在不同节点间传递对象
二、Java序列化的基本实现
2.1 实现Serializable接口
要使一个类可序列化,只需实现java.io.Serializable
接口(该接口是一个标记接口,没有方法)。
import java.io.Serializable;public 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;}// Getters and setterspublic String getName() { return name; }public int getAge() { return age; }@Overridepublic String toString() {return "Person{name='" + name + "', age=" + age + "}";}
}
2.2 使用ObjectOutputStream进行序列化
import java.io.*;public class SerializationExample {public static void main(String[] args) {Person person = new Person("张三", 30);try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {// 序列化对象oos.writeObject(person);System.out.println("对象序列化成功");} catch (IOException e) {e.printStackTrace();}}
}
2.3 使用ObjectInputStream进行反序列化
import java.io.*;public class DeserializationExample {public static void main(String[] args) {try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {// 反序列化对象Person person = (Person) ois.readObject();System.out.println("对象反序列化成功");System.out.println(person);} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}
}
三、序列化的高级特性
3.1 serialVersionUID的作用
serialVersionUID
是一个标识序列化类版本的静态常量,用于在反序列化时验证类的兼容性。如果不指定,Java会根据类的结构自动生成一个,但建议显式声明以避免版本不一致问题。
private static final long serialVersionUID = 1L;
3.2 瞬态关键字transient
使用transient
关键字修饰的字段不会被序列化,反序列化后该字段的值为默认值。
import java.io.Serializable;public class User implements Serializable {private static final long serialVersionUID = 1L;private String username;private transient String password; // 密码字段不被序列化public User(String username, String password) {this.username = username;this.password = password;}// Getters and setterspublic String getUsername() { return username; }public String getPassword() { return password; }@Overridepublic String toString() {return "User{username='" + username + "', password='" + password + "'}";}
}
3.3 自定义序列化方法
可以通过在类中定义以下两个特殊方法来自定义序列化过程:
private void writeObject(ObjectOutputStream out)
private void readObject(ObjectInputStream in)
import java.io.*;public class CustomSerialization implements Serializable {private static final long serialVersionUID = 1L;private int value;public CustomSerialization(int value) {this.value = value;}// 自定义序列化方法private void writeObject(ObjectOutputStream out) throws IOException {// 写入原始值的加密版本out.writeInt(value * 2);}// 自定义反序列化方法private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {// 读取加密值并解密value = in.readInt() / 2;}public int getValue() {return value;}
}
3.4 实现Externalizable接口
Externalizable
接口继承自Serializable
,提供了更细粒度的序列化控制。实现该接口需要重写writeExternal
和readExternal
方法。
import java.io.*;public class Employee implements Externalizable {private static final long serialVersionUID = 1L;private String name;private int employeeId;// 必须提供无参构造函数public Employee() {}public Employee(String name, int employeeId) {this.name = name;this.employeeId = employeeId;}@Overridepublic void writeExternal(ObjectOutput out) throws IOException {out.writeObject(name);out.writeInt(employeeId);}@Overridepublic void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {name = (String) in.readObject();employeeId = in.readInt();}@Overridepublic String toString() {return "Employee{name='" + name + "', employeeId=" + employeeId + "}";}
}
四、序列化的最佳实践
4.1 始终声明serialVersionUID
为每个可序列化的类显式声明serialVersionUID
,以确保版本兼容性。
4.2 处理敏感字段
使用transient
关键字标记敏感字段,避免在序列化过程中泄露信息。
4.3 实现readObjectNoData方法
在类中实现readObjectNoData()
方法,以处理反序列化时没有数据的情况。
private void readObjectNoData() throws ObjectStreamException {// 初始化对象的默认状态this.name = "默认名称";this.age = 0;
}
4.4 序列化单例和枚举
对于单例类,应确保反序列化不会创建新的实例,可以通过实现readResolve()
方法:
private Object readResolve() throws ObjectStreamException {return Singleton.getInstance();
}
枚举类型本身就支持序列化,无需特殊处理。
4.5 序列化集合和数组
集合和数组如果包含可序列化的元素,则它们本身也是可序列化的。
import java.io.*;
import java.util.ArrayList;
import java.util.List;public class CollectionSerializationExample {public static void main(String[] args) {List<Person> people = new ArrayList<>();people.add(new Person("张三", 30));people.add(new Person("李四", 40));try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("people.ser"))) {oos.writeObject(people);} catch (IOException e) {e.printStackTrace();}try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("people.ser"))) {@SuppressWarnings("unchecked")List<Person> deserializedPeople = (List<Person>) ois.readObject();for (Person person : deserializedPeople) {System.out.println(person);}} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}
}
五、序列化的常见问题与解决方案
5.1 NotSerializableException
当尝试序列化未实现Serializable
接口的类时,会抛出此异常。解决方案是确保所有需要序列化的类都实现Serializable
接口。
5.2 InvalidClassException
当序列化版本不一致或类结构发生变化时,可能会抛出此异常。解决方案是显式声明serialVersionUID
,并在类结构变化时谨慎处理。
5.3 性能问题
Java原生序列化性能较低,特别是对于大量数据。可以考虑使用更高效的序列化框架,如JSON、Protobuf、Kryo等。
5.4 安全风险
反序列化不受信任的数据可能导致安全漏洞,如远程代码执行。应避免反序列化来自不可信源的数据,或使用安全的序列化框架。
六、替代序列化方案
6.1 JSON序列化
JSON是一种轻量级的数据交换格式,广泛用于Web应用中。可以使用Jackson、Gson等库实现Java对象与JSON的互转。
import com.fasterxml.jackson.databind.ObjectMapper;public class JsonSerializationExample {public static void main(String[] args) throws Exception {ObjectMapper mapper = new ObjectMapper();Person person = new Person("张三", 30);// 对象转JSONString json = mapper.writeValueAsString(person);System.out.println("JSON: " + json);// JSON转对象Person deserializedPerson = mapper.readValue(json, Person.class);System.out.println("对象: " + deserializedPerson);}
}
6.2 Protobuf序列化
Protobuf是Google开发的高效序列化框架,具有高性能和小体积的特点。
// 定义.proto文件
syntax = "proto3";message Person {string name = 1;int32 age = 2;
}// 使用Protobuf生成的类进行序列化和反序列化
PersonProto.Person person = PersonProto.Person.newBuilder().setName("张三").setAge(30).build();// 序列化
byte[] data = person.toByteArray();// 反序列化
PersonProto.Person deserializedPerson = PersonProto.Person.parseFrom(data);
6.3 Kryo序列化
Kryo是一个快速高效的Java序列化框架,支持自定义序列化策略。
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;public class KryoSerializationExample {public static void main(String[] args) {Kryo kryo = new Kryo();kryo.register(Person.class);Person person = new Person("张三", 30);// 序列化try (Output output = new Output(new FileOutputStream("person.kryo"))) {kryo.writeObject(output, person);} catch (IOException e) {e.printStackTrace();}// 反序列化try (Input input = new Input(new FileInputStream("person.kryo"))) {Person deserializedPerson = kryo.readObject(input, Person.class);System.out.println(deserializedPerson);} catch (IOException e) {e.printStackTrace();}}
}
若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!
ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ
相关文章:
Java-IO流之序列化与反序列化详解
Java-IO流之序列化与反序列化详解 一、序列化与反序列化概述1.1 基本概念1.2 核心接口与类1.3 应用场景 二、Java序列化的基本实现2.1 实现Serializable接口2.2 使用ObjectOutputStream进行序列化2.3 使用ObjectInputStream进行反序列化 三、序列化的高级特性3.1 serialVersion…...
机器学习14-迁移学习
迁移学习学习笔记 一、迁移学习概述 迁移学习是机器学习中的一个重要领域,它旨在解决当目标任务的训练数据有限时,如何利用与目标任务相关但不完全相同的源任务数据来提高学习性能的问题。在现实世界中,获取大量高质量的标注数据往往成本高…...

CAN通信收发测试(USB2CAN模块测试实验)
1.搭建测试环境 电脑:安装 USB 驱动,安装原厂调试工具,安装cangaroo(参考安装包的入门教程即可) USB驱动路径:~\CAN分析仪资料20230701_Linux\硬件驱动程序 原厂调试工具路径:~\CAN分析仪资料2…...
小白初学SpringBoot记录
1.对于通过json返回用户信息时,需要忽略password字段操作: 1.1 pom配置jackson细节: <dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>…...

OSCP备战-BSides-Vancouver-2018-Workshop靶机详细步骤
一、靶机介绍 靶机地址:https://www.vulnhub.com/entry/bsides-vancouver-2018-workshop%2C231/ 靶机难度:中级(CTF) 靶机发布日期:2018年3月21日 靶机描述: Boot2root挑战旨在创建一个安全的环境&…...

PDF转Markdown/JSON软件MinerU最新1.3.12版整合包下载
MinerU发布至今我已经更新多版整合包了,5天前MinerU发布了第一个正式版1.0.1,并且看到在18小时之前有更新模型文件,我就做了个最新版的一键启动整合包。 2025年02月21日更新v1.1.0版整合包 2025年02月27日更新v1.2.0版整合包 2025-06-05 更…...
Android第十三次面试总结基础
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...

【深入学习Linux】System V共享内存
目录 前言 一、共享内存是什么? 共享内存实现原理 共享内存细节理解 二、接口认识 1.shmget函数——申请共享内存 2.ftok函数——生成key值 再次理解ftok和shmget 1)key与shmid的区别与联系 2)再理解key 3)通过指令查看/释放系统中…...

编程基础:执行流
能帮到你的话,就给个赞吧 😘 文章目录 执行流同步:顺序执行,只有一个执行流异步:新开后台(次)执行流,后台执行流要确保不能影响主执行流。共有两个执行流。 阻塞:任务阻塞执行流,导致…...

理解非结构化文档:将 Reducto 解析与 Elasticsearch 结合使用
作者:来自 Elastic Adel Wu 演示如何将 Reducto 的文档处理与 Elasticsearch 集成以实现语义搜索。 Elasticsearch 与业界领先的生成式 AI 工具和提供商有原生集成。欢迎观看我们的网络研讨会,了解如何超越 RAG 基础,或使用 Elastic 向量数据…...
算法训练第十天
232. 用栈实现队列 代码: class MyQueue(object):def __init__(self):self.arr1 []self.arr2 []def push(self, x):""":type x: int:rtype: None"""self.arr1.append(x)def pop(self):""":rtype: int""…...
2种官方方法关闭Windows防火墙
2种官方方法关闭Windows防火墙 引言一、防火墙:你电脑的"智能安检员"二、这些场景,可能需要"临时撤防"三、极速关闭方案方法一:通过系统设置(Win10/11专属通道)方法二:通过传统控制面板(全系统通用:Win7-11全系)四、 必读安全警告(关闭前请三思!…...

[面试精选] 0094. 二叉树的中序遍历
文章目录 1. 题目链接2. 题目描述3. 题目示例4. 解题思路5. 题解代码6. 复杂度分析 1. 题目链接 94. 二叉树的中序遍历 - 力扣(LeetCode) 2. 题目描述 给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。 3. 题目示例 示例 1 : 输入&…...
股指期货期权交易规则是什么?
本文主要介绍股指期货期权交易规则是什么?股指期货期权是以股指期货合约为标的物的期权交易,其规则结合了期货与期权的特点。 股指期货期权交易规则是什么? 一、基础交易规则 交易时间 交易日9:30-11:30,13:00-15:00࿰…...

学习笔记(23): 机器学习之数据预处理Pandas和转换成张量格式[1]
学习笔记(23): 机器学习之数据预处理Pandas和转换成张量格式[1] 学习机器学习,需要学习如何预处理原始数据,这里用到pandas,将原始数据转换为张量格式的数据。 1、安装pandas pip install pandas 2、写入和读取数据 >>创建一个人工…...

2025年6月6日第一轮
2025年6月6日 The rapid in Chiese industdy is developnig e,and it is From be in a enjoy a deep is developing The drone industry in China is developing The drone industy in china develops rapidly and is in a leading position in in the world. The dro…...
记一次运行spark报错
提交spark任务运次报错 06/03 18:27:50 INFO Client: Setting up container launch context for our AM 25/06/03 18:27:50 INFO Client: Setting up the launch environment for our AM container 25/06/03 18:27:50 INFO Client: Preparing resources for our AM container …...

12-Oracle 23ai Vector 使用ONNX模型生成向量嵌入
一、Oracle 23ai Vector Embeddings 核心概念 向量嵌入(Vector Embeddings) -- 将非结构化数据(文本/图像)转换为数值向量 - - 捕获数据的语义含义而非原始内容 - 示例:"数据库" → [0.24, -0.78, 0.5…...
2. 库的操作
2.1 创建数据库 语法: CREATE DATABASE [IF NOT EXISTS] db_name [create_specification [, create_specification] ...] create_specification: [DEFAULT] CHARACTER SET charset_name # 字符集: 存储编码 [DEFAULT] COLLATE collation_name # 校验集: 比较/选择/读…...

pytorch 与 张量的处理
系列文章目录 文章目录 系列文章目录一、Tensor 的裁剪二、Tensor 的索引与数据筛选torch.wheretorch.indicestorch.gathertorch.masked_selecttorch.taketorch.nonzero(省略) 三、Tensor 的组合与拼接torch.cattorch.stack 四、Tensor的切片chunksplit …...

layer norm和 rms norm 对比
Layer norm # Layer Norm 公式 mean x.mean(dim-1, keepdimTrue) var x.var(dim-1, keepdimTrue) output (x - mean) / sqrt(var eps) * gamma beta特点: 减去均值(去中心化)除以标准差(标准化)包含可学习参数 …...

Java高级 | 【实验六】Springboot文件上传和下载
隶属文章:Java高级 | (二十二)Java常用类库-CSDN博客 系列文章:Java高级 | 【实验一】Springboot安装及测试 |最新-CSDN博客 Java高级 | 【实验二】Springboot 控制器类相关注解知识-CSDN博客 Java高级 | 【实验三】Springboot 静…...
RKNN开发环境搭建1-基于Ubuntu 18.04系统使用Docker安装rknn-toolkit2
目录 写在最前面Docker 方式安装rknn-toolkit2写在最前面 瑞芯微在RKNN的环境搭建方面的资料很多,但是在搭建过程中发现很多问题教程中并未提及,对初学者不友好。所以博主做了这个系列的文章,从开始搭建环境到对于RKNN Model Zoo的示例进行实践,希望能对初学者有帮助。坚持…...
qt使用笔记二:main.cpp详解
Qt中main.cpp文件详解 main.cpp是Qt应用程序的入口文件,包含程序的启动逻辑。下面我将详细解析其结构和功能。 基本结构 一个典型的Qt main.cpp 文件结构如下: #include <QApplication> // 或者 QGuiApplication/QCoreApplication #include &…...

VBA进度条ProgressForm1
上一章《VBA如何使用ProgressBar进度条控件》介绍了ProgressBar控件的使用方法,今天我给大家介绍ProgressForm1进度条的使用方法,ProgressForm1是集成ProgressBar控件和Label控件的窗体,可以同时显示进度条和百分比,如下图&#x…...

行为型设计模式之Interpreter(解释器)
行为型设计模式之Interpreter(解释器) 前言: 自己的话理解:自定义一个解释器用来校验参数或数据是否合法。 1)意图 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解…...

深入解析 CAS 操作
一、CAS 的本质:硬件级别的乐观锁 CAS(Compare-And-Swap,比较并交换) 是一种原子操作指令,用于实现对共享变量的无锁并发修改。它是现代多核处理器支持的底层硬件指令,也是构建高效并发数据结构࿰…...

vue3+TS+eslint9配置
记录eslint升级到9.x的版本之后遇到的坑 在 ESLint 9 中,配置方式发生了变化。Flat Config 格式(eslint.config.js 或 .ts)不再支持 extensions 选项。所以vscode编辑器中的 extensions 需要注释掉,要不然保存的时候不会格式化。…...

【bug】Error: /undefinedfilename in (/tmp/ocrmypdf.io.9xfn1e3b/origin.pdf)
在使用ocrmypdf的时候,需要Ghostscript9.55及以上的版本,但是ubuntu自带为9.50 然后使用ocrmypdf报错了 sudo apt update sudo apt install ghostscript gs --version 9.50 #版本不够安装的版本为9.50不够,因此去官网https://ghostscript.c…...
HarmonyOS5.0——CodeGenie:鸿蒙生态的AI编程革命
CodeGenie:鸿蒙生态的AI编程革命 华为推出的 CodeGenie 是集成于 DevEco Studio 的 AI 辅助编程工具,专为 HarmonyOS 应用开发设计。它通过深度优化 ArkTS 和 C 语言的代码生成能力,显著提升开发效率,降低鸿蒙生…...