【Java基础】序列化、反序列化和不可变类
Hi~!这里是奋斗的明志,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~
🌱🌱个人主页:奋斗的明志
🌱🌱所属专栏:Java基础面经
📚本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为展示我的学习过程及理解。文笔、排版拙劣,望见谅。

Java基础
- 一、Java中的序列化和反序列化是什么?
- 1、序列化
- 1.1 Java序列化关键类和接口
- 2、反序列化
- 3、注意点
- 3.1 transient 关键字
- 3.2 serialVersionUID
- 3.3 序列化性能问题
- 3.4 安全性
- 4、序列化和反序列化理解
- 4.1 Java 序列化 Serializable 的意义
- 4.2 serialVersionUID 又有什么用?
- 4.2 Java 序列化不包含静态变量
- 二、什么是 Java 中的不可变类?
- 1、特征
- 1.1 不可变类 Person 实现
- 1.2 验证不可变性
- 2、不可变类的优缺点
- 2.1 优点
- 2.2 缺点
- 3、举例 String
一、Java中的序列化和反序列化是什么?
应用场景:
网络传输、远程调用、持久化存储(比如:保存数据到文件或者数据库)、以及分布式系统中数据交换。
1、序列化
在Java中 序列化就是把
对象转换为字节流
这样对象可以通过网络传输、持久化存储或者缓存
Java 提供了java.io.Serializable接口来支持序列化,只要类实现了这个接口,就可以将该类的对象进行序列化.
1.1 Java序列化关键类和接口
- 通过实现
Serializable接口,然后用ObjectOutputStream和ObjectInputStream ObjectOutputStream用于序列化,ObjectInputStream用于反序列化- 类必须 实现
Serializable接口 才能被序列化
2、反序列化
是将字节流重新转换为对象的过程,即从存储中读取数据,并重新创建对象
3、注意点
3.1 transient 关键字
- 在序列化过程中,有些字段不需要被序列化,例如:敏感数据,可以使用
transient关键字 标记不需要序列化的字段。
3.2 serialVersionUID
- 每一个
Serializable类都应该定义一个serialVersionUID,用于在反序列化时验证版本的一致性。如果没有明确指定serialVersionUID,Java会根据类的定义自动生成一个 UID ,版本不匹配可能导致反序列化失败。
3.3 序列化性能问题
- Java默认的序列化机制可能比较慢,尤其是对于大规模分布式系统,可能会选择更加高效的序列化框架
- 比如:
Protobuf、Kryo等
3.4 安全性
- 反序列化是一个潜在的安全风险,因为通过恶意构造的字节流,可能会加载不安全的类 或者 执行不是期望的代码。因此,反序列化过程需要进行输入验证,避免反序列化漏洞。
4、序列化和反序列化理解
序列化其实就是将对象转化为可传输的字节序列格式,以便于存储和传输。
- 因为对象在 JVM 中可以认为是“立体的”,会有各种引用。
- 比如在内存地址中 Ox666 引用了某某某对象,呢此时这个对象要传输到网络的另一端的时候,就需要把这些引用“压扁”。
- 因为网络另一端的内存地址 Ox666 可以没有某某某对象,所以传输的对象需要包含这些信息,然后接收端将这些扁平的信息再反序列化得到对象。
- 所以,反序列化就是将字节序列格式转换为对象的过程

4.1 Java 序列化 Serializable 的意义

Serializable 接口没有什么实际的含义,就是起到一个标记的作用
- 观看一下源码就会非常清楚,除了String,数组,枚举之外,如果实现了 serializable 这个接口就走 writeOrdinaryObject,否则序列化就会抛出异常。

4.2 serialVersionUID 又有什么用?
private static final long serialVersionUID = 1L;
想必经常会看到这样的代码,这个 ID 其实就是用来验证序列化的对象和反序列化对应的对象的 ID 是否是一致的。所以这个 ID 的数字其实不重要,无论是 1L 还是 idea 自动生成的,只要序列化的时候对象 serialVersionUID和反序列化的时候对象的 serialVersionUlD 一致的话就行。如果没有显式指定 serialVersionUlD 则编译器会根据类的相关信息自动生成一个,可以认为是一个指纹。所以如果你没有定义一个 serialVersionUID 然后序列化个对象之后,在反序列化之前把对象的类的结构改了,比如增加了一个成员变量,则此时的反序列化会失败。因为类的结构变了,生成的指纹就变了,所以 serialVersionUID 就不一致了.
4.2 Java 序列化不包含静态变量
简单地说就是序列化之后存储的内容不包含静态变量的值,看一下下面的代码就很清晰了。
import java.io.*;public class Test implements Serializable {//指纹private static final long serialVersionUID = 1L;public static int n = 1;public static void main(String[] args) {try {ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("明志学编程!"));outputStream.writeObject(new Test());outputStream.close();//序列化后修改值Test.n = 2;ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("明志学编程!"));Test t = (Test) objectInputStream.readObject();objectInputStream.close();System.out.println(t.n);} catch (Exception e) {e.printStackTrace();}}
}

二、什么是 Java 中的不可变类?
不可变类 是指在创建后其状态(对象的字段)无法被修改的类。一旦对系被创建,它的所有属性都不能被修改。也就是说,对象的所有成员变量在初始化后就不能被修改,无论是通过外部方法还是内部方法。这种类的实例整个生命周期内保持不变。
1、特征
- 字段声明为 final,防止子类继承
- 该类的所有字段都是 private 和 final,确保只能在构造函数中初始化,之后不能修改。另外,不要提供 serter 方法,因为 setter 会改变变量的值。
- 如果类中有可变对象的引用,比如一个 Date 对象,那么在获取该对象的时候应该返回其副本而不是原对象,防止外部修改影响内部状态。
- 通过构造函数初始化所有字段
- 如果有一个可变对象,比如一个Date类型的birthDate,那么在getter中应该返回birthDate的克隆或者新的Date对象,而不是直接返回引用,这样可以避免外部的修改影响到Person类内部的状态。
- 可能还需要注意
深拷贝的问题,特别是在构造函数中传入可变对象时,应该进行拷贝,而不是直接引用。比如,如果构造函数的参数是Date,应该创建一个新的Date对象保存其值,这样外部的Date对象后续变化不会影响Person内部的birthDate。 - 另外,确保类不会被扩展,所以类本身要声明为final,这样不会有子类覆盖方法导致状态变化的风险。
Java中经典不可变类有 :
String,Integer,BigDecimal,LocalDate等
1.1 不可变类 Person 实现
public final class Person {// 1. 成员变量声明为 private finalprivate final String name;private final int age;private final List<String> hobbies;// 2. 通过构造函数初始化所有成员变量public Person(String name, int age, List<String> hobbies) {this.name = name;this.age = age;// 3. 对可变对象进行深拷贝(防御性拷贝)this.hobbies = new ArrayList<>(hobbies);}// 4. 不提供 setter 方法public String getName() {return name;}public int getAge() {return age;}// 5. 返回可变对象的不可变视图或深拷贝public List<String> getHobbies() {return Collections.unmodifiableList(hobbies);// 或者返回深拷贝:return new ArrayList<>(hobbies);}
}
1.2 验证不可变性
public class Main {public static void main(String[] args) {List<String> hobbies = new ArrayList<>();hobbies.add("Reading");Person person = new Person("Alice", 30, hobbies);// 尝试修改原始 hobbieshobbies.add("Cooking");System.out.println("Original Hobbies: " + hobbies); // [Reading, Cooking]// 不可变类的 hobbies 未被修改System.out.println("Person's Hobbies: " + person.getHobbies()); // [Reading]// 尝试通过 getHobbies() 修改(会抛出 UnsupportedOperationException)person.getHobbies().add("Gaming");}
}

2、不可变类的优缺点
2.1 优点
- 线程安全:由于不可变对象的状态不能被修改,他们天生就是线程安全的,在并发环境中无需同步。
- 缓存友好:不可变对象可以安全的被缓存和共享,如 string 的字符常量池。
- 防止状态不一致:不可变类可以有效避免因意外修改对象状态而导致的不一致问题。
2.2 缺点
- 性能问题:不可变对象需要在每次状态变化时创建新的对象,这可能会导致性能开销,尤其是对于大规模对象或频繁修改的场景(例如String频繁拼接)
3、举例 String
String就是典型的不可变类,当你创建一个String对象之后,这个对象就无法被修改。
因为无法被修改,所以像执行S += “a” ,这样的方法,其实返回的是一个新建的String对象,老的 S 指向的对象不会发生变化,只是 S 的引用指向了新的对象而已。
所以不要在字符串拼接频繁的场景使用+来拼接,因为这样会频繁的创建对象。
不可变类的好处就是安全,因为知晓这个对象不可能会被修改,因此可以放心大胆的用,在多线程环境下也是线程安全的。
查看 String 的源码,发现 String 类是 final 修饰的,表示无法被继承。
String本质是一个char数组,然后用final修饰,不过 final 限制不了数组内部的数据,所以这还不够。所以value是用private修饰的,并且没有暴露出set方法,这样外部其实就接触不到vlue所以无法修改。
当然还是有修改的需求,比如replace方法,所以这时候就需要返回一个新对象来作为结果。

就是私有化变量,然后不要暴露St方法,即使有修改的需求也是返回一个新对象。


相关文章:
【Java基础】序列化、反序列化和不可变类
Hi~!这里是奋斗的明志,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~ 🌱🌱个人主页:奋斗的明志 🌱🌱所属专栏:Java基础面经 📚本系列文章为个…...
R语言 文本分析 天龙八部
起因, 目的: 前面有人对 “倚天屠龙记” 进行分析,我这里只是进行模仿而已。 完整的文件, 已经绑定了,反正读者可以找一下。 案例背景 小说《天龙八部》是金庸先生所著的武侠小说,也是“射雕三部曲”的前传。全书共50章,字数超过一百万字。故事发生在北宋末年,以大理…...
Gradle 发布Jar到 Maven Central
1. 前置步骤 自 2024 年 3 月 12 日起,所有注册和发布都将通过 Central Portal 进行。 Central Portal 帐户注册命名空间可用于对工件进行签名的 GPG 密钥 1. 创建 GPG 密钥对 2. 分发公钥 更多教程参考本文: 如何发布jar包到maven中央仓库(2024年3月最…...
小米6的ubantu touch 怎么安装docker
deekseek 真的太牛逼了。 小米6的ubantu touch 怎么安装docker? Thought for 19 seconds 嗯,用户问的是在小米6上安装Ubuntu Touch后如何安装Docker。首先,我需要确认Ubuntu Touch的版本是否支持Docker。Ubuntu Touch是基于Ubuntu的移动操作…...
USB子系统学习(四)使用libusb读取鼠标数据
文章目录 1、声明2、HID协议2.1、描述符2.2、鼠标数据格式 3、应用程序4、编译应用程序5、测试 1、声明 本文是在学习韦东山《驱动大全》USB子系统时,为梳理知识点和自己回看而记录,全部内容高度复制粘贴。 韦老师的《驱动大全》:商品详情 …...
深度剖析 Redisson 分布式锁:原理、实现与应用实践
文章目录 写在文章开头详解Redisson 分布式锁使用和实现前置准备工作分布式锁的基本使用公平锁的使用联锁的使用读写锁基本使用常见问题Redisson和Jedis有什么区别redisson如何实现分布式锁redisson如何实现分布式锁的可重入redisson如何实现公平锁Redisson的watchdog机制是什么…...
Android Studio 配置 Gerrit Code Review
很多大厂(华为、荣耀)的大型项目都有gerrit代码审查流程,那么我们如何实现不手动敲命令行,就在Android Studio中像平常开发一样,只需要用鼠标点点点,就能将代码推送到gerrit审查仓呢,现在就来跟…...
基于微信小程序的医院预约挂号系统的设计与实现
hello hello~ ,这里是 code袁~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹 🦁作者简介:一名喜欢分享和记录学习的在校大学生…...
仅128个token达到ImageNet生成SOTA性能!MAETok:有效的扩散模型的关键是什么?(卡内基梅隆港大等)
论文链接:https://arxiv.org/pdf/2502.03444 项目链接:https://github.com/Hhhhhhao/continuous_tokenizer 亮点直击 理论与实验分析:通过实验和理论分析建立了潜空间结构与扩散模型性能之间的联系。揭示了具有更少高斯混合模型(G…...
AWS成本优化实战:查询未关联弹性IP地址的完整指南
一、背景介绍 1.1 为什么要管理未关联的弹性IP 弹性IP (Elastic IP) 是AWS中的重要资源,但未关联的弹性IP会产生不必要的成本支出。主要问题包括: 闲置资源浪费持续产生费用增加运维成本影响资源管理效率1.2 解决方案价值 通过自动化脚本定期检查未关联的弹性IP: 及时发现…...
I2C协议—读写EEPROM(24Cxx为例)
STM32 I2C协议详解与应用实践-CSDN博客 实际项目中移植IIC协议及读写EEprom代码: /* AT24C08,写次数达100万次SOC ,2500次充放电次数,需要500万次记录,需存储均衡 每页*/#include "FreeRTOS.h" #include "task.h" #incl…...
示例:JAVA调用deepseek
近日,国产AI DeepSeek在中国、美国的科技圈受到广泛关注,甚至被认为是大模型行业的最大“黑马”。在外网,DeepSeek被不少人称为“神秘的东方力量”。1月27日,DeepSeek应用登顶苹果美国地区应用商店免费APP下载排行榜,在…...
Linux系统命令无法使用(glib库相关问题)
1.背景描述 Yum强制安装了一些软件,安装软件成功无报错,完成后不久突然发现系统出问题了,所有的命令无法使用了,如ls、mv、cat等基本命令报错。 relocation error: /lib64/libpthread.so.0: symbol_libc_dl_error_tsd …...
Java 一键将 Word 文档转为 PDF
嘿,朋友们!在开发中,经常会碰到需要把 Word 文档转换成 PDF 格式的需求,像生成报告、合同啥的。Java 有不少好用的库能实现这个功能,下面就给大家介绍两种常见的方法,分别使用 Apache POI 和 Docx4J 结合 i…...
从基础到进阶:一文掌握排序、查找、动态规划与图算法的全面实现(C++代码实例解析)
引言 算法是计算机科学的核心,也是程序员解决复杂问题的利器。从基础的排序与查找到进阶的动态规划与图论算法,掌握这些技能不仅是提升编程能力的必经之路,更是解决实际问题的根本。本篇文章将通过 C 实现多个经典算法,包括排序、…...
Nginx反代Ollama接口跨域、无法逐字输出问题
场景 本地部署deepseek模型,用的Ollama管理,内网穿透到公网,在通过nginx反代ollama接口。 问题描述 跨域问题 nginx转发时请求头中需要加入origin,并且origin还要和ollama接口同源(协议、ip、端口一致)。…...
电脑黑屏按什么键恢复?电脑黑屏的解决办法
电脑黑屏的原因有很多,可能是硬件、软件、系统或者病毒等方面造成的。那么,当我们遇到电脑黑屏时,应该怎么做呢?有没有什么快捷的方法可以恢复正常呢?本文将为您介绍一些常见的电脑黑屏情况及其解决办法。 一、电脑开机…...
docker启动报错code=exited, status=1/FAILURE——问题排查
问题 在某台centos7机器上,启动docker服务 sudo systemctl start docker报下列错误: ● docker.service - Docker Application Container EngineLoaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)Active: …...
Kong故障转移参数配置
一、Passive Health Check Healthchecks.Passive.Unhealthy.HttpStatuses 含义: 列出了被认为是“不健康”的HTTP状态码。目的: 当健康检查(Healthcheck)返回这些状态码时,系统会认为服务不健康,并可能触…...
使用opencv解析视频,通过图片比对,筛选出每一帧视频的变化
记录瞬间 最近碰到一个问题,在客户端上操作时,存在背景判断的情况,对自动化实现此操作增加难度。 所以考虑到实际的使用,将一些计算机视觉技术加入到实际的使用中,来解决此问题。 import os import cv2 import numpy#…...
思翼遥控器疑问?
1.地面端与遥控端对频,地面端选择数传2为串口,天空端的UART2通过USB转TTL模块连接电脑,通过串口助手观察得有1Hz输出帧(开启遥控器APP时间段为10Hz),共21字节,请问,这个是什么含义&a…...
anaconda中可以import cv2,但是notebook中cv2 module not found
一、问题 anaconda中成功import cv2 但是jupyter notebook中却无法导入cv2 二、排查 anaconda中使用python路径如下: jupyter notebook中使用python路径如下: 可以发现路径不一致。 三、解决 ①查看可用的kernel ②选中想要修改的kernel,打…...
如何解决 Linux 文件系统挂载失败的问题
当遇到Linux文件系统挂载失败的问题时,您可以通过以下步骤来解决问题: 解决方法: 检查挂载点: 确保要挂载的目标文件系统存在,并且挂载点是正确的。检查挂载点是否已经被其他文件系统占用。 检查文件系统状态&#x…...
PHP填表统计预约打卡表单系统小程序
📋 填表统计预约打卡表单系统——专属定制,信息互动新纪元 📊 填表统计预约打卡表单系统,一款专为现代快节奏生活量身打造的多元化自定义表单统计小程序,集信息填表、预约报名、签到打卡、活动通知、报名投票、班级统…...
PAT乙级( 1009 说反话 1010 一元多项式求导)C语言版本超详细解析
1009 说反话 给定一句英语,要求你编写程序,将句中所有单词的顺序颠倒输出。 输入格式: 测试输入包含一个测试用例,在一行内给出总长度不超过 80的字符串。字符串由若干单词和若干空格组成,其中单词是由英文字母&#x…...
LVSNAT服务搭建
LVSNAT实验环境搭建 在虚拟机上,我的NAT模式ip划分为:172.25.254.0 仅主机模式IP为:192.168.0.0 拓补图如下 配置服务:LVS服务端添加两个网卡,分别为NAT模式和仅主机模式 LVS服务端配置: systemctl st…...
websocket自动重连封装
websocket自动重连封装 前端代码封装 import { ref, onUnmounted } from vue;interface WebSocketOptions {url: string;protocols?: string | string[];reconnectTimeout?: number; }class WebSocketService {private ws: WebSocket | null null;private callbacks: { [k…...
2. Mellanox 网卡的参数调优-LINK_TYPE_P1(GPU-AI-大模型,底层调优-测试)
命令详细分析 echo yes | sudo mlxconfig -d $line set LINK_TYPE_P1=1 这个命令用于设置 Mellanox 网卡设备的 LINK_TYPE_P1 参数为 1。以下是该命令的详细解析: 各部分解释 echo yes |: 这个部分通过管道将字符串 yes 传递给后续命令,以自动确认任何需要用户输入确认的…...
apisix网关ip-restriction插件使用说明
ip-restriction插件可以在网关层进行客户端请求ip拦截。 当然了,一般不推荐使用该方法,专业的事专业工具做。建议有条件,还是上防火墙或者waf来做。 官方文档:ip-restriction | Apache APISIX -- Cloud-Native API Gateway whit…...
使用 Docker 和 PM2 构建高并发 Node.js API 网关
在现代 Web 开发中,构建高并发、高可用的 API 网关是一个常见的需求。本文将介绍如何结合 Docker 和 PM2 构建一个高性能的 Node.js API 网关,并深入探讨分布式限流器的原理与实现。 1. 背景与需求 1.1 高并发 API 网关的挑战 在高并发场景下ÿ…...

