关于序列化和反序列化

什么是序列化,什么是反序列化

简单来说:

  • 序列化:将数据结构或对象转换成二进制字节流的过程
  • 反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程

为什么要进行序列化

我们要将java对象进行网络传输,或者持久化到文件或者文件,数据库或者缓存当中的时候,我们就要进行序列化。将java对象序列化为二进制字节数据。

下面是序列化和反序列化常见应用场景:

  • 对象在进行网络传输(比如远程方法调用 RPC 的时候)之前需要先被序列化,接收到序列化的对象之后需要再进行反序列化;
  • 将对象存储到文件之前需要进行序列化,将对象从文件中读取出来需要进行反序列化;
  • 将对象存储到数据库(如 Redis)之前需要用到序列化,将对象从缓存数据库中读取出来需要反序列化;
  • 将对象存储到内存之前需要进行序列化,从内存中读取出来之后需要进行反序列化。

序列化的主要目的是通过网络传输对象或者说是将对象存储到文件系统、数据库、内存中。

如何进行序列化

JDK自带的序列化

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Builder
@ToString
public class RpcRequest implements Serializable {private static final long serialVersionUID = 1905122041950251207L;private String requestId;private String interfaceName;private String methodName;private Object[] parameters;private Class<?>[] paramTypes;private RpcMessageTypeEnum rpcMessageTypeEnum;
}

serialVersionUID 有什么作用?

序列化号 serialVersionUID 属于版本控制的作用。反序列化时,会检查 serialVersionUID 是否和当前类的 serialVersionUID 一致。如果 serialVersionUID 不一致则会抛出 InvalidClassException 异常。强烈推荐每个序列化类都手动指定其 serialVersionUID,如果不手动指定,那么编译器会动态生成默认的 serialVersionUID

static 修饰的变量是静态变量,位于方法区,本身是不会被序列化的。但是,serialVersionUID 的序列化做了特殊处理,在序列化时,会将 serialVersionUID 序列化到二进制字节流中;在反序列化时,也会解析它并做一致性判断。

为什么不推荐使用 JDK 自带的序列化?

我们很少或者说几乎不会直接使用 JDK 自带的序列化方式,主要原因有下面这些原因:

  • 不支持跨语言调用 : 如果调用的是其他语言开发的服务的时候就不支持了。
  • 性能差:相比于其他序列化框架性能更低,主要原因是序列化之后的字节数组体积较大,导致传输成本加大。
  • 存在安全问题:序列化和反序列化本身并不存在问题。但当输入的反序列化的数据可被用户控制,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码。

比较常用的序列化协议有 Hessian、Kryo、Protobuf、ProtoStuff,这些都是基于二进制的序列化协议。这里不做详细说明,可自行查询

如何不让某一个变量不进行序列化

对于不想进行序列化的变量,使用 transient 关键字修饰。

transient 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。

关于 transient 还有几点注意:

  • transient 只能修饰变量,不能修饰类和方法。
  • transient 修饰的变量,在反序列化后变量值将会被置成类型的默认值。例如,如果是修饰 int 类型,那么反序列后结果就是 0
  • static 变量因为不属于任何对象(Object),所以无论有没有 transient 关键字修饰,均不会被序列化。

探究transient是否能够序列化

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;/*** @author 微信公众号:Java技术栈*/
public class TransientTest {public static void main(String[] args) throws Exception {User user = new User();user.setUsername("Java技术栈");user.setId("javastack");System.out.println("\n序列化之前");System.out.println("username: " + user.getUsername());System.out.println("id: " + user.getId());ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("d:/user.txt"));os.writeObject(user);os.flush();os.close();ObjectInputStream is = new ObjectInputStream(new FileInputStream("d:/user.txt"));user = (User) is.readObject();is.close();System.out.println("\n序列化之后");System.out.println("username: " + user.getUsername());System.out.println("id: " + user.getId());}
}/*** @author 微信公众号:Java技术栈*/
class User implements Serializable {private static final long serialVersionUID = 1L;private String username;private transient String id;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getId() {return id;}public void setId(String id) {this.id = id;}}

结果

序列化之前
username: Java技术栈
id: javastack
 
序列化之后
username: Java技术栈
id: null 

示例1在 id 字段上加了 transient 关键字修饰,反序列化出来之后值为 null,说明了被 transient 修饰的变量不能被序列化。

但是 transient只作用于实现 Serializable 接口;

import java.io.Externalizable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;/*** @author 微信公众号:Java技术栈*/
public class ExternalizableTest {public static void main(String[] args) throws Exception {User3 user = new User3();user.setUsername("Java技术栈");user.setId("javastack");ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream(new File("javastack")));objectOutput.writeObject(user);ObjectInput objectInput = new ObjectInputStream(new FileInputStream(new File("javastack")));user = (User3) objectInput.readObject();System.out.println(user.getUsername());System.out.println(user.getId());objectOutput.close();objectInput.close();}}/*** @author 微信公众号:Java技术栈*/
class User3 implements Externalizable {private static final long serialVersionUID = 1L;public User3() {}private String username;private transient String id;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getId() {return id;}public void setId(String id) {this.id = id;}@Overridepublic void writeExternal(ObjectOutput objectOutput) throws IOException {objectOutput.writeObject(id);}@Overridepublic void readExternal(ObjectInput objectInput) throws IOException, ClassNotFoundException {id = (String) objectInput.readObject();}}

结果

null
javastack

示例3的 id 被 transient 修改了,为什么还能序列化出来?那是因为 User3 实现了接口 Externalizable,而不是 Serializable。

在 Java 中有两种实现序列化的方式,Serializable 和 Externalizable,可能大部分人只知道 Serializable 而不知道 Externalizable。

这两种序列化方式的区别是:实现了 Serializable 接口是自动序列化的,实现 Externalizable 则需要手动序列化,通过 writeExternal 和 readExternal 方法手动进行,这也是为什么上面的 username 为 null 的原因了。

transient 关键字总结

1)transient修饰的变量不能被序列化;

2)transient只作用于实现 Serializable 接口;

3)transient只能用来修饰普通成员变量字段;

探究static是否能够序列化

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;/*** @author 微信公众号:Java技术栈*/
public class TransientStaticTest {public static void main(String[] args) throws Exception {User2 user = new User2();User2.username = "Java技术栈1";user.setId("javastack");System.out.println("\n序列化之前");System.out.println("username: " + user.getUsername());System.out.println("id: " + user.getId());ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("d:/user.txt"));os.writeObject(user);os.flush();os.close();// 在反序列化出来之前,改变静态变量的值User2.username = "Java技术栈2";ObjectInputStream is = new ObjectInputStream(new FileInputStream("d:/user.txt"));user = (User2) is.readObject();is.close();System.out.println("\n序列化之后");System.out.println("username: " + user.getUsername());System.out.println("id: " + user.getId());}
}/*** @author 微信公众号:Java技术栈*/
class User2 implements Serializable {private static final long serialVersionUID = 1L;public static String username;private transient String id;public String getUsername() {return username;}public String getId() {return id;}public void setId(String id) {this.id = id;}}

结果

序列化之前
username: Java技术栈1
id: javastack
 
序列化之后
username: Java技术栈2
id: null

示例2把 username 改为了 public static, 并在反序列化出来之前改变了静态变量的值,结果可以看出序列化之后的值并非序列化进去时的值。

由以上结果分析可知,静态变量不能被序列化,示例2读取出来的是 username 在 JVM 内存中存储的值 ,不管有没有 transient 修饰,静态变量都不能被序列化;

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

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

相关文章

TorchAcc:基于 TorchXLA 的分布式训练框架

演讲人&#xff1a;林伟&#xff0c;阿里云研究员&#xff0c;阿里云人工智能平台 PAI 技术负责人 本文旨在探讨阿里云 TorchAcc&#xff0c;这是一个基于 PyTorch/XLA 的大模型分布式训练框架。 过去十年 AI 领域的显著进步&#xff0c;关键在于训练技术的革新和模型规模的快…

详细剖析多线程2----线程安全问题(面试高频考点)

文章目录 一、概念二、线程不安全的原因三、解决线程不安全问题--加锁&#xff08;synchronized&#xff09;synchronized的特性 四、死锁问题五、内存可见性导致的线程安全问题 一、概念 想给出⼀个线程安全的确切定义是复杂的&#xff0c;但我们可以这样认为&#xff1a; 在多…

国际结算-汇出汇款和汇入汇款

目录 汇出汇款业务 汇入汇款 汇出汇款业务 汇出汇款业务是本行接受汇款人的委托,以约定的汇款方式委托海外代理行将一定金额的款项付给指定收款人的业务。用于满足国际间资金汇划结算需求。汇款方式包括电汇、信汇和票汇,目前常用的是电汇。 业务特点,费用少,与信用证和托…

C语言 strcmp

在C语言中&#xff0c;strcmp 函数用于比较两个字符串。它的原型定义在 <string.h> 头文件中。strcmp 函数比较两个字符串直到找到一个不同的字符或者到达字符串的末尾。函数的原型如下&#xff1a; int strcmp(const char *str1, const char *str2); 参数 str1&#…

立体统计图表绘制方法(凸显式环图)

立体统计图表绘制方法&#xff08;凸显式环图&#xff09; 记得我学统计学的时候&#xff0c;那些统计图表大都是平面的框框图&#xff0c;很呆板&#xff0c;就只是表现出统计的意义就好了。在网络科技发展进步的当下&#xff0c;原来一些传统的统计图表都有了进一步的创新。在…

RDGCN翻译

RDGCN翻译 Relation-Aware Entity Alignment for Heterogeneous Knowledge Graphs 面向异质知识图谱的关系感知实体对齐 阅读时间&#xff1a;2024.03.24 领域&#xff1a;知识图谱&#xff0c;知识对齐 作者&#xff1a;Yuting Wu等人 PKU 出处&#xff1a;IJCAI Abstract…

[精选]Kimi到底是什么,将带来什么?

## 阿里通义千问重磅升级&#xff1a;免费开放1000万字长文档处理功能。 Kimi突然的泼天富贵&#xff0c;大家都想沾一把。短期这一块大概率会继续热一段时间。 作为月之暗面的创始人&#xff0c;杨植麟常把他的AGI梦想形容为“登月计划”&#xff0c;长文本就是这个伟大计划…

HarmonyOS NEXT应用开发之听歌识曲水波纹特效案例

介绍 在很多应用中&#xff0c;会出现点击按钮出现水波纹的特效。 效果图预览 使用说明 进入页面&#xff0c;点击按钮&#xff0c;触发水波纹动画。再次点击按钮&#xff0c;停止水波纹动画。 实现思路 本例涉及的关键特性和实现方案如下&#xff1a; 要实现存在两个连续…

基于AWS云服务构建智能家居系统的最佳实践

在当今智能家居时代,构建一个安全、高性能、可扩展和灵活的智能家居系统已经成为许多公司的目标。亚马逊网络服务(AWS)提供了一系列云服务,可以帮助企业轻松构建和管理智能家居系统。本文将探讨如何利用AWS云服务构建一个智能家居系统,并分享相关的最佳实践。 系统架构概述 该…

科研学习|论文解读——这取决于你什么时候搜索(MIS Quarterly,2022)

原文题目 It Depends on When you search 摘要 互联网搜索已被证明对股票价格、公司销售和疫情传播具有强大的预测能力。在研究提出搜索频率作为投资者关注的更直接和及时的衡量指标之后,我们探索了搜索数据的异质性,并解决了当前文献中的几个问题。使用来自谷歌的标准普尔50…

C++ - 类和对象(上)

目录 一、类的定义 二、访问限定符 public&#xff08;公有&#xff09; protected&#xff08;保护&#xff09; private&#xff08;私有&#xff09; 三、类声明和定义分离 四、外部变量和成员变量的区别与注意 五、类的实例化 六、类对象的模型 七、类的this指针…

TCP详解

一、TCP报文段结构 1、源端口号和目的端口号都是16位&#xff0c;范围从&#xff08;1-65535&#xff0c;0不可用&#xff09; 2、序列号&#xff1a;在建立连接时由内核生成的随机数作为其初始值&#xff0c;通过 SYN 报文传给接收端主机&#xff0c;每发送一次数据&#xff0…

C语言数据结构易错知识点(5)(插入排序、选择排序)

插入排序&#xff1a;直接插入排序、希尔排序 选择排序&#xff1a;直接选择排序、堆排序 上述排序都是需要掌握的&#xff0c;但原理不会讲解&#xff0c;网上有很多详尽地解释&#xff0c;本文章主要分享一下代码实现上应当注意的事项 1.直接插入排序&#xff1a; 代码实…

拥抱C++的深度和复杂性,挖掘更多可能 !——《C++20高级编程(第5版)》

&#xff0c;C难以掌握&#xff0c;但其广泛的功能使其成为游戏和商业软件应用程序中最常用的语言。即使是有经验的用户通常也不熟悉许多高级特性&#xff0c;但C20的发布提供了探索该语言全部功能的绝佳机会。《C20高级编程(第5版)》为C的必要内容提供了一个代码密集型、面向解…

Redis 教程系列之Redis Java 使用 Redis(十一)

安装 开始在 Java 中使用 Redis 前&#xff0c; 我们需要确保已经安装了 redis 服务及 Java redis 驱动&#xff0c;且你的机器上能正常使用 Java。 Java的安装配置可以参考我们的 Java 开发环境配置 接下来让我们安装 Java redis 驱动&#xff1a; 首先你需要下载驱动包 下载…

Qt笔记 计时器

下面介绍设计计时器的两种方法&#xff0c;分别是利用信号与槽来使用计时器&#xff0c;利用计时器事件来使用计时器。 1. 利用信号与槽来使用计时器 //方法一(利用信号与槽来使用计时器):QTimer *timer new QTimer(this);timer->start(1000);//周期&#xff0c;计时器每隔…

(AtCoder Beginner Contest 325) ---- D - Printing Machine -- 题解

目录 D - Printing Machine&#xff1a; 题目大意&#xff1a; 思路解析&#xff1a; 代码实现&#xff1a; D - Printing Machine&#xff1a; 题目大意&#xff1a; 思路解析&#xff1a; 打印一次后&#xff0c;需要充电一微秒后才能再次打印就可以看作每微妙只能打印一…

【文献阅读】AlphaFold touted as next big thing for drug discovery — but is it?

今天来精读2023年10月发在《Nature》上的一篇新闻&#xff1a;AlphaFold touted as next big thing for drug discovery — but is it? (nature.com)https://www.nature.com/articles/d41586-023-02984-w Questions remain about whether the AI tool for predicting protein …

蓝桥杯基础练习详细讲解二(具体代码、解题思路、Python)

试题 基础练习 回文数 提交此题 评测记录 资源限制 内存限制&#xff1a;512.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 1221是一个非常特殊的数&#xff0c;它从左边读和从右边读是一样的&#x…

Day29:学习SpringCloud

学习计划&#xff1a;完成尚硅谷的尚上优选项目 学习进度&#xff1a;完成尚上优选项目的前置知识点&#xff1a;SpringCloud 知识点&#xff1a; MQ高级 消息可靠性 生产者消息确认消息持久化消费者消息确认消费失败重试机制 死信交换机 初识死信交换机TTL延迟队列