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

article/2025/9/16 23:28:50

我们都知道,新建一个对象的时候实现 Serializeable 接口,但为什么要这么做?什么时候这样子做?这样子做会不会出现幺蛾子?阿粉一个三连差点把自己都问懵逼了……

那接下来,大家就和阿粉一起简单了解一下这个知识点吧……

序列化的定义是:将一个对象编码成一个字节流(I/O);而与之相反的操作被称为反序列化。

序列化的目的是为了方便数据的传递以及存储到磁盘上(把一个Java对象写入到硬盘或者传输到网路上面的其它计算机,这时我们就需要将对象转换成字节流才能进行网络传输。对于这种通用的操作,就出现了序列化来统一这些格式)。

简单来说序列化就是一种用来处理对象流的机制。将对象转化成字节序列后可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在。

使用场景:所有可在网络上传输的对象都必须是可序列化的,比如RMI(remote method invoke,即远程方法调用),传入的参数或返回的对象都是可序列化的,否则会出错;所有需要保存到磁盘的java对象都必须是可序列化的。比如 Redis 将对象当做字符串存储的时候,如果对象实现了序列化,则只需要将对象直接存储即可(java会自动将对象转换成序列化后的字节流);否则需要自己将对象转换成 json 字符串存储,不过 json 字符串相对更加节省内存空间一些。

通常建议:程序创建的每个JavaBean类都实现 Serializeable 接口。但是实现 Serializeable 接口也需要小心谨慎。正如《Effective Java》中第 74 条提到的那样:

如何实现序列化

在 Java 中,只要一个类实现了 java.io.Serializable 接口,它就可以被序列化(枚举类也可以被序列化)。

例如:每个枚举类型都会默认继承类java.lang.Enum,而Enum类实现了Serializable接口,所以枚举类型对象都是默认可以被序列化的。

// DeletedEnum 类,表示删除状态
@Getter
@AllArgsConstructor
public enum DeletedEnum {NO_DELETED(0,"未删除"),DELETED(1,"已删除");public final Integer status;public final String name;
}

下图是 java.lang.Enum 类:

而一个普通的类想实现序列化,只需要实现 Serializable 接口即可:

@Data
public class User implements Serializable {//序列化版本号private static final long serialVersionUID = 1111013L;transient private String name;private int age;public static void main(String[] args) {User user = new User();user.setAge(12);user.setName("小路飞");System.out.println(user);}
}

输出结果如下:

User(name=小路飞, age=12)

那为什么一个类实现了 Serializable 接口,它就可以被序列化呢?

这是因为它使用 ObjectOutputStream 来持久化对象到文件中,使用了 writeObject 方法,该方法又调用了如下方法:

    /*** Underlying writeObject/writeUnshared implementation.*/private void writeObject0(Object obj, boolean unshared) throws IOException {……// remaining casesif (obj instanceof String) {writeString((String) obj, unshared);} else if (cl.isArray()) {writeArray(obj, desc, unshared);} else if (obj instanceof Enum) {writeEnum((Enum<?>) obj, desc, unshared);} else if (obj instanceof Serializable) {writeOrdinaryObject(obj, desc, unshared);} else {if (extendedDebugInfo) {throw new NotSerializableException(cl.getName() + "\n" + debugInfoStack.toString());} else {throw new NotSerializableException(cl.getName());}}……}

从上述代码可知,如果被写对象的类型是String,或数组,或 Enum,或 Serializable,那么就可以对该对象进行序列化,否则将抛出 NotSerializableException。

:String 类型的对象、枚举类型的对象、数组对象,都是默认可以被序列化的,并生成默认的序列化版本号。

我看网上的资料都有讲使用 Externalizable 接口和使用 transient 关键字来实现序列化的方法,但阿粉感觉用的不多,所以在这里就不过多的赘述了,况且它们其实都是基于 Serializable 接口实现的序列化。

如何自动生成序列化版本号

idea IDE

安装 serialVersionUID 插件即可。

eclipse

一般来说有两种生成方式:

  • 一个是默认的1L,比如:private static final long serialVersionUID = 1L;

  • 一个是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段。实现序列化后,类名上会出现黄色波浪下划线,选择第一项,添加已生成的串行版本标识即可自动生成一个。

序列化版本号的用处

在 java.io.Serializable 的文档中的解释是这样的:

大致意思是如下

因为反序列化必须拥有 class 文件,但随着项目的升级,class 文件也会升级,序列化怎么保证升级前后的兼容性呢?

序列化运行时与每个可序列化的类关联一个版本号,称为 serialVersionUID,在反序列化期间使用该版本号来验证序列化对象的发送者和接收者是否已加载了该对象的与序列化兼容的类。如果接收方已为该对象加载了一个与相应发送方类具有不同的 serialVersionUID 的类,则反序列化将导致 InvalidClassException。可序列化的类可以通过声明一个名为 serialVersionUID 的字段来显式声明其自己的 serialVersionUID,该字段必须是静态的,最终的且类型为 long:

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

只要版本号相同,即使更改了序列化属性,对象也可以正确被反序列化回来。

@Data
public class User implements Serializable {//序列化版本号    private static final long serialVersionUID = 1111013L;private String name;private int age;
}

如果反序列化使用的 class 的版本号与序列化时使用的不一致,反序列化会报 InvalidClassException 异常。

如果可序列化的类未明确声明 serialVersionUID ,则序列化运行时将根据该类的各个方面为该类计算默认的 serialVersionUID 值,如Java(TM)对象序列化规范中所述。但是,强烈建议所有可序列化的类显式声明 serialVersionUID 值,因为默认的 serialVersionUID 计算对类详细信息高度敏感,而类详细信息可能会根据编译器的实现而有所不同,因此可能在反序列化期间导致意外的 InvalidClassExceptions。

而且,默认值不利于 jvm 间的移植,可能class文件没有更改,但不同 jvm 可能计算的规则不一样,这样也会导致无法反序列化。

因此,为了保证不同Java编译器实现之间的 serialVersionUID 值一致,可序列化的类必须声明一个显式的 serialVersionUID 值。强烈建议显式 serialVersionUID 声明在可能的情况下使用 private 修饰符,因为此类声明仅适用于立即声明的类 serialVersionUID字段作为继承成员不起作用。

最后,使用默认机制在序列化对象时,不仅会序列化当前对象,还会对该对象引用的其它对象也进行序列化,同样地,这些其它对象引用的另外对象也将被序列化,以此类推。所以,如果一个对象包含的成员变量是容器类对象,而这些容器所含有的元素也是容器类对象,那么这个序列化的过程就会较复杂,开销也较大。

参考

  • https://www.cnblogs.com/kubixuesheng/p/10350523.html

  • https://stackoverflow.com/questions/285793/what-is-a-serialversionuid-and-why-should-i-use-it

  • https://juejin.im/post/5ce3cdc8e51d45777b1a3cdf

  • 《Effective Java》中文版第二版

< END >

如果大家喜欢我们的文章,欢迎大家转发,点击在看让更多的人看到。也欢迎大家热爱技术和学习的朋友加入的我们的知识星球当中,我们共同成长,进步。


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

相关文章

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

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基本不再建议…

在什么情况下方法调用会被内联?

写在前面 本文隶属于专栏《100个问题搞定Java虚拟机》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和文献引用请见100个问题搞定Java虚拟机 解答 方法内联有许多规则。…

【C++】 内联函数详解(搞清内联的本质及用法)

目录 一.什么是内联函数 1.直观上定义&#xff1a; 2.更深入的思考&#xff1a; 二.为什么使用内联函数 1.为什么要代替部分宏定义 2.普通函数频繁调用的过程消耗栈空间 3.更深入的思考 三.内联函数和编译过程的相爱相杀 四.内联函数怎么用&#xff0c;在哪儿用&#…

[C++] 内联函数inline 以及 auto关键字 -- C++入门(4)

本篇文章主要包括内联函数和auto关键字。其中&#xff0c;内敛函数包括概念&#xff0c;特性等&#xff1b;auto关键字的使用规则&#xff0c;使用场景等。 目录 1.内敛函数 1.1问题引入&#xff1a; 1.2内联函数的概念 1.3内敛函数的特性 2.auto关键字 2.1auto简介 2.2 auto的…