java序列化详解

article/2025/9/17 1:30:31
一、序列化与反序列化

序列化:指堆内存中的java对象数据,通过某种方式把对存储到磁盘文件中,或者传递给其他网络节点(网络传输)。这个过程称为序列化,通常是指将数据结构或对象转化成二进制的过程。

即将对象转化为二进制,用于保存,或者网络传输。

反序列化:把磁盘文件中的对象数据或者把网络节点上的对象数据,恢复成Java对象模型的过程。也就是将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程

与序列化相反,将二进制转化成对象。
二、序列化的作用

① 想把内存中的对象保存到一个文件中或者数据库中时候;
② 想用套接字在网络上传送对象的时候;
③ 想通过RMI传输对象的时候

一些应用场景,涉及到将对象转化成二进制,序列化保证了能够成功读取到保存的对象。
三、java的序列化实现

要实现对象的序列化,最直接的操作就是实现Serializable接口

使用IO流中的对象流可以实现序列化操作,将对象保存到文件,再读取出来。

首先创建一个对象,并实现Serializable接口:

import java.io.Serializable;public class User implements Serializable{private static final long serialVersionUID = 1L;private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User [name=" + name + ", age=" + age + "]";}}

用对象流写一个保存对象与读取对象的工具类:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;public class SerializeUtil {// 保存对象,序列化public static void saveObject(Object object) throws Exception {ObjectOutputStream out = null;FileOutputStream fout = null;try {fout = new FileOutputStream("D:/1.txt");out = new ObjectOutputStream(fout);out.writeObject(object);} finally {fout.close();out.close();}}// 读取对象,反序列化public static Object readObject() throws Exception {ObjectInputStream in = null;FileInputStream fin = null;try {fin = new FileInputStream("D:/1.txt");in = new ObjectInputStream(fin);Object object = in.readObject();return object;} finally {fin.close();in.close();}}
}

测试:


public class Main {public static void main(String[] args) {User user = new User();user.setName("旭旭宝宝");user.setAge(33);// 保存try {SerializeUtil.saveObject(user);} catch (Exception e) {System.out.println("保存时异常:" + e.getMessage());}// 读取User userObject;try {userObject = (User) SerializeUtil.readObject();System.out.println(userObject);} catch (Exception e) {System.out.println("读取时异常:" + e.getMessage());}}
}

测试结果:
这里写图片描述

这里我们成功的进行了一次将对象保存到文件中,再读取了出来。如果此时,我们不实现序列化接口,就会出现异常了。我们取消实现的Serialiable接口代码:


public class User {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User [name=" + name + ", age=" + age + "]";}}

再测试Main方法:
这里写图片描述
可以看到此时报错了,此时使用 e.printStackTrace();查看异常的详细信息:
这里写图片描述
可以看到Unknown Source,因为没有进行序列化,所以无法进行保存与读取。

四、序列化ID的作用

可以看到,我们在进行序列化时,加了一个serialVersionUID字段,这便是序列化ID

 private static final long serialVersionUID = 1L;

这个序列化ID起着关键的作用,它决定着是否能够成功反序列化!java的序列化机制是通过判断运行时类的serialVersionUID来验证版本一致性的,在进行反序列化时,JVM会把传进来的字节流中的serialVersionUID与本地实体类中的serialVersionUID进行比较,如果相同则认为是一致的,便可以进行反序列化,否则就会报序列化版本不一致的异常。

即序列化ID是为了保证成功进行反序列化
五、默认的序列化ID

当我们一个实体类中没有显式的定义一个名为“serialVersionUID”、类型为long的变量时,Java序列化机制会根据编译时的class自动生成一个serialVersionUID作为序列化版本比较,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID。譬如,当我们编写一个类时,随着时间的推移,我们因为需求改动,需要在本地类中添加其他的字段,这个时候再反序列化时便会出现serialVersionUID不一致,导致反序列化失败。那么如何解决呢?便是在本地类中添加一个“serialVersionUID”变量,值保持不变,便可以进行序列化和反序列化。


如果没有显示指定serialVersionUID,会自动生成一个。


只有同一次编译生成的class才会生成相同的serialVersionUID


但是如果出现需求变动,Bean类发生改变,则会导致反序列化失败。为了不出现这类的问题,所以我们最好还是显式的指定一个serialVersionUID。

六、序列化的其他问题
  1. 静态变量不会被序列化( static,transient)
  2. 当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口。
  3. 当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化。
子类序列化时:如果父类没有实现Serializable接口,没有提供默认构造函数,那么子类的序列化会出错;如果父类没有实现Serializable接口,提供了默认的构造函数,那么子类可以序列化,父类的成员变量不会被序列化。如果父类实现了Serializable接口,则父类和子类都可以序列化。
七、使用效率更高的序列化框架—Protostuff

其实java的原生序列化方式(通过实现Serialiable接口),效率并不是最高的。
github上有一个分析序列化效率的项目:https://github.com/eishay/jvm-serializers/wiki
这里写图片描述
其中看的出来性能最优的为google开发的colfer ,但是由于colfer的使用难度太大,而更多的都是使用protostuff序列化框架。适用改框架要引入两个库(core与runtime)。

①github地址:https://github.com/protostuff/protostuff

③如果用Maven,则添加依赖:

<dependency><groupId>io.protostuff</groupId><artifactId>protostuff-core</artifactId><version>1.5.9</version>
</dependency>
<dependency><groupId>io.protostuff</groupId><artifactId>protostuff-core</artifactId><version>1.5.9</version>
</dependency>

修改Main代码


import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtobufIOUtil;
import com.dyuproject.protostuff.ProtostuffIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;public class Main {public static void main(String[] args) {User user = new User();user.setName("旭旭宝宝");user.setAge(33);Schema<User> schema = RuntimeSchema.getSchema(User.class);// 保存对象,序列化,转化二进制数据LinkedBuffer buffer = LinkedBuffer.allocate(512);final byte[] protostuff;try {protostuff = ProtobufIOUtil.toByteArray(user, schema, buffer);} finally {buffer.clear();}// 读取对象,反序列化User userObject = schema.newMessage();ProtostuffIOUtil.mergeFrom(protostuff, userObject, schema);System.out.println(userObject);}
}

User类,并未实现Serializable接口

public class User {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User [name=" + name + ", age=" + age + "]";}}

测试结果
这里写图片描述
若要要整合Redis使用,也可以写成一个工具类:


import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;import com.dyuproject.protostuff.LinkedBuffer;
import com.dyuproject.protostuff.ProtobufIOUtil;
import com.dyuproject.protostuff.Schema;
import com.dyuproject.protostuff.runtime.RuntimeSchema;public class SerializeUtil {private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<>();@SuppressWarnings("unchecked")public static <T> byte[] serializer(T obj) {Class<T> clazz = (Class<T>) obj.getClass();Schema<T> schema = getSchema(clazz);return ProtobufIOUtil.toByteArray(obj, schema, LinkedBuffer.allocate(256));}public static <T> T deSerializer(byte[] bytes, Class<T> clazz) {T message;try {message = clazz.newInstance();} catch (InstantiationException | IllegalAccessException e) {throw new RuntimeException(e);}Schema<T> schema = getSchema(clazz);ProtobufIOUtil.mergeFrom(bytes, message, schema);return message;}@SuppressWarnings("unchecked")public static <T> Schema<T> getSchema(Class<T> clazz) {Schema<T> schema = (Schema<T>) cachedSchema.get(clazz);if (schema == null) {schema = RuntimeSchema.createFrom(clazz);if (schema != null) {cachedSchema.put(clazz, schema);}}return schema;}
}

这样即使我们的User类就不用再实现Serialiable接口了,同样可以进行序列化,效率也更高。
对于Redis的工具类封装,参照:https://blog.csdn.net/qq_35890572/article/details/81664911

[参考博客]:https://www.cnblogs.com/ysocean/p/6870069.html


http://chatgpt.dhexx.cn/article/ycLvcT1O.shtml

相关文章

序列化和反序列化的底层实现原理是什么?

序列化和反序列化作为Java里一个较为基础的知识点&#xff0c;大家心里也有那么几句要说的&#xff0c;但我相信很多小伙伴掌握的也就是那么几句而已&#xff0c;如果再深究问一下Java如何实现序列化和反序列化的&#xff0c;就可能不知所措了&#xff01;遥记当年也被问了这一…

序列化和反序列化

我以前确实对序列化&#xff0c;乃至现在也是不是很熟悉&#xff0c;有时候查找资料&#xff0c;但依旧懵懵懂懂&#xff0c;不过还好遇到一个博主&#xff0c;确定写的挺好的&#xff0c;链接会放再底部 废话不多说&#xff0c;先看官网定义&#xff1a; 序列化 (Serializat…

我把序列化玩成了这样,吊锤了一波面试官

我们都知道&#xff0c;新建一个对象的时候实现 Serializeable 接口&#xff0c;但为什么要这么做&#xff1f;什么时候这样子做&#xff1f;这样子做会不会出现幺蛾子&#xff1f;阿粉一个三连差点把自己都问懵逼了…… 那接下来&#xff0c;大家就和阿粉一起简单了解一下这个…

什么是序列化? 如何实现(反)序列化 序列化的应用

1. 什么是序列化与反序列化&#xff0c;什么情况需要序列化1.1 序列化序列化是什么序列化的目的什么情况需要序列化 1.2 反序列化反序列化是什么反序列化的目的 2. Java中的序列化与反序列化2.1 如何实现序列化Java序列化的规定序列化的API实现(反)序列化的示例对象在硬盘上的存…

1.传输线驻波比

Transmission Line & Active Voltage Standing Wave Ratio 1.1 信号完整性概述 数字电路的出现极大地提高了电子产品的抗干扰能力&#xff0c;随着电路的工作频率不断提高&#xff0c;这种抗干扰能力逐渐显得有些“力不从心”。特别是在高速电路的范畴&#xff0c;“理想互…

驻波比,功率计原理,短波机驻波测量

文章内容转载自http://bbs.cqcqcq.com/thread-1627-1-1.html 衡量功率反射大小的量称为「反射系数」&#xff0c;常用Γ (音 gamma) 或ρ (音 rho) 表示。为了讨论简单起见&#xff0c;我们假设负载阻抗为纯阻性的。反射系数定义为&#xff1a; ρ (反射电压波) / (入射电压波)…

入射波反射波和驻波的特性推导

入射波反射波和驻波的基本推导 学习雷达过程中&#xff0c;发现阻抗匹配是一道迈不过去的坎&#xff0c;而阻抗匹配、能量传输与电压驻波比又有千丝万缕的联系&#xff0c;而电压驻波比则与反射波、入射波等相关的特性有关&#xff0c;于是写下此文章记录一下推导过程。 懒得正…

反射系数、驻波比、S参数之间的关系

反射系数、驻波比、S参数之间的关系&#xff01; 转载▼ 回波损耗(Return Loss): 入射功率/反射功率, 为dB数值 反射系数(Г): 反射电压/入射电压, 为标量 电压驻波比(Voltage Standing Wave Ration): 波腹电压/波节电压 S参数: S12为反向传输系数&#xff0c;也就是隔离。…

馈线中的VSWR电压驻波比

在射频信号馈线传输中&#xff0c;信号传输有一个概念&#xff1a;驻波比。 这个概念好理解&#xff0c;就是一个波进去&#xff0c;在终端由于不匹配形成反射波回来。 但是不是那么好想像&#xff0c;叠加后是啥模样的波形。 借助pythonmatplotlib可以方便模拟出来&#xf…

简单了解什么是驻波比?

驻波比全称为电压驻波比&#xff0c;又名VSWR&#xff0c;为英文Voltage Standing Wave Ratio的简写&#xff0c;在理解电压驻波比之前先要明白什么是“驻波”。 假设两个波长相同的波以相反的方向传播&#xff0c;绿线波朝着左方向旋转&#xff0c;蓝线波朝着右方向旋转&…

驻波比理解

VSWR(Voltage Standing Wave Ratio)代表电压驻波比。要完全理解这个术语&#xff0c;需要知道什么是“驻波”。 假设两个波长相同的波以相反的方向传播&#xff0c;如下所示。一个波表示为蓝线&#xff0c;它朝着正确的方向旋转。另一个波用绿线表示&#xff0c;它在左方向旋转…

电压驻波比,回波损耗,传输损耗,电压反射系数,功率传输,功率反射换算表

回波损耗(Return Loss)&#xff1a;入射功率/反射功率, 为dB数值反射系数(Г)&#xff1a;反射电压/入射电压, 为标量电压驻波比(Voltage Standing Wave Ration): 波腹电压/波节电压S参数&#xff1a;S12为反向传输系数&#xff0c;也就是隔离。S21为正向传输系数&#xff0c;也…

天线的驻波比

驻波比是衡量天线性能的重要参数之一,体现了天线向外界空间辐射能量的潜力。这是一个标量的参数,还有史密斯圆图(the Smith Chart)来衡量天线的阻抗特性,可以分析天线是感性还是容性的,并指明了调整天线的方向。 目录​ 一、什么是驻波比VSWR或者SWR

关于驻波比(VSWR)的详细解析

from滤波器 ◆ ◆ ◆ 文 | 滤波器&#xff08;ID:Filter_CN&#xff09; 驻波比&#xff08;VSWR&#xff09;用来检测天馈线系统、射频接头以及所有的连接到基站的射频设备的工作状态。VSWR过高会导致掉话、高误码率&#xff0c;而且由此引入的发射/接受功率的衰减会导致小…

射频回波损耗、反射系数、电压驻波比、S参数的含义与关系

以二端口网络为例&#xff0c;如单根传输线&#xff0c;共有四个S参数&#xff1a;S11&#xff0c;S12&#xff0c;S21&#xff0c;S22&#xff0c;对于互易网络有S12&#xff1d;S21&#xff0c;对于对称网络有S11&#xff1d;S22&#xff0c;对于无耗网络&#xff0c;有S11*S…

【回波损耗(dB)和电压驻波比(VSWR)之间的关系】

回波损耗(dB)和电压驻波比(VSWR)之间的关系 反射系数&#xff08;Г / Rho&#xff09; Г&#xff1d;反射波振幅/入射波振幅 &#xff1d;(传输线特性阻抗-负载阻抗) / (传输线特性阻抗负载阻抗) 回波损耗( RL ) 回波损耗&#xff1a; 回波损耗&#xff0c;又称为反射损…

RF(射频) - VSWR(电压驻波比)

VSWR代表电压驻波比&#xff08;Voltage Standing Wave Ratio&#xff09;。要完全理解这个术语&#xff0c;你需要知道什么是“驻波”。 你可能已经在高中物理课上学到了驻波。只要刷新你的想法&#xff0c;让我解释一下驻波是什么。 假设具有相同波长的两个波沿相反方向传播…

天线参数-自用1

天线参数 1丶 天线谐振频率 Resonance Frequency 2丶驻波比 指的是行驻波的电压波腹值和电压波节值之比 2.1 驻波 驻波即两个反方向波的合成波形&#xff0c;该合成波相位不变&#xff0c;幅度变化&#xff0c;节点位置&#xff08;值 0&#xff09;不会发生变化。 幅度最大…

内联函数(inline)总结

1&#xff1a;定义&#xff1a; 它们看起来象函数&#xff0c;运作起来象函数&#xff0c;比宏(macro)要好得多&#xff0c;使用时还不需要承担函数调用的开销。当内联一个函数时&#xff0c;编译器可以对函数体执行特定环境下的优化工作。这样的优化对"正常"的…

【C++】内联函数理解

内联函数 内联函数的使用是对于C语言中宏函数的一种改进&#xff0c;他继承了宏的优点并避免了宏的缺点。 宏的优点&#xff1a;a. 代码复用性高 b. 宏函数减少栈帧建立&#xff0c;提高效率 宏的缺点&#xff1a;a. 可读性差 b. 没有类型安全检查 c. 不方便调试 C基本不再建议…