【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 接口,然后用 ObjectOutputStreamObjectInputStream
  • 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方法,即使有修改的需求也是返回一个新对象。

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

吴恩达深度学习——卷积神经网络的特殊应用

内容来自https://www.bilibili.com/video/BV1FT4y1E74V&#xff0c;仅为本人学习使用。 文章目录 人脸识别相关定义Similarity函数使用Siamese网络实现函数d使用Triplet损失学习参数 神经风格迁移深度卷积网络可视化神经风格迁移的代价函数内容损失函数风格损失函数 人脸识别 …

搭建linux qt5.6环境

文章目录 准备工作步骤测试 准备工作 1、linux虚拟机环境 2、linux qt安装包相关文件&#xff0c;本文采用压缩包方式 步骤 1&#xff09;启动虚拟机并登入 2&#xff09;打开linux终端命令行&#xff0c;ifconfig获取当前linux环境的ip 3&#xff09;使用WinSCP设置好ip、…

C++ 使用CURL开源库实现Http/Https的get/post请求进行字串和文件传输

CURL开源库介绍 CURL 是一个功能强大的开源库&#xff0c;用于在各种平台上进行网络数据传输。它支持众多的网络协议&#xff0c;像 HTTP、HTTPS、FTP、SMTP 等&#xff0c;能让开发者方便地在程序里实现与远程服务器的通信。 CURL 可以在 Windows、Linux、macOS 等多种操作系…

【产品小白】用户调研的需求是否都采纳?

在用户调研中&#xff0c;并非所有需求都应被直接采纳&#xff0c;而应通过系统分析转化为符合产品战略的有效决策。以下是关键思考框架&#xff1a; 1. 用户需求 ≠ 产品需求 矛盾性&#xff1a;用户个体需求可能相互冲突&#xff08;如A功能的去留&#xff09;&#xff0c;需…

如何导入第三方sdk | 引入第三方jar 包

0. 背景1. 上传私有仓库2. 使用本地文件系统 0. 背景 对接一些第三方功能&#xff0c;会拿到第三方的sdk&#xff0c;也就是jar包&#xff0c;如何导入呢 1. 上传私有仓库 最好的方式就是将第三方jar包&#xff0c;上传到私有的仓库&#xff0c;这样直接正常在pom引用即可如果只…

基础入门-网站协议身份鉴权OAuth2安全Token令牌JWT值Authirization标头

知识点&#xff1a; 1、网站协议-http/https安全差异&#xff08;抓包&#xff09; 2、身份鉴权-HTTP头&OAuth2&JWT&Token 一、演示案例-网站协议-http&https-安全测试差异性 1、加密方式 HTTP&#xff1a;使用明文传输&#xff0c;数据在传输过程中可以被…

07苍穹外卖之redis缓存商品、购物车(redis案例缓存实现)

课程内容 缓存菜品 缓存套餐 添加购物车 查看购物车 清空购物车 功能实现&#xff1a;缓存商品、购物车 效果图&#xff1a; 1. 缓存菜品 1.1 问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压…

DeepSeek-R1 本地大模型搭建对接API

DeepSeek-R1 在这里将学到很多知识 欢迎使用使用DeepSeek-R1本地大模型DeepSeek 的模型基础说明DeepSeek的本地 API 说明DeepSeek 本地模型搭建1、执行命令安装及测试 DeepSeek-R1 API接口调用当然&#xff0c;我们为了让用户更加便捷&#xff0c;我们把API 接口全部放到上面截…

【0404】Postgres内 实现分配一个新的 Object ID (OID)

文章目录 1. 分配一个新 Object ID (OID)1.1 ShmemVariableCache 中 nextOid1.2 写一个 NEXTOID log record1. 分配一个新 Object ID (OID) Postgres内核中分配一个新的 Oid 是由函数 GetNewObjectId() 实现。该函数声明于 transam.h,实现于 varsup.c 源文件。 对于 GetNewO…

250207-MacOS修改Ollama模型下载及运行的路径

在 macOS 上&#xff0c;Ollama 默认将模型存储在 ~/.ollama/models 目录。如果您希望更改模型的存储路径&#xff0c;可以通过设置环境变量 OLLAMA_MODELS 来实现。具体步骤如下&#xff1a; 选择新的模型存储目录&#xff1a;首先&#xff0c;确定您希望存储模型的目标目录路…

Ubuntu20.4软件应用打不开

安装 snap-store&#xff1a; 确保 Snap 已安装&#xff1a; Snap 是一个包管理系统&#xff0c;需要先确保 snapd 已经安装。如果系统中没有安装&#xff0c;可以通过以下命令来安装 Snap&#xff1a; sudo apt update sudo apt install snapd安装 snap-store&#xff1a; 使…

深入解析:React 事件处理的秘密与高效实践

在 React 中&#xff0c;事件处理是构建交互式应用的核心。本文将带你深入探索 React 事件处理的机制、最佳实践以及如何避免常见陷阱&#xff0c;助你写出更高效、更健壮的代码。 1. React 事件处理的独特之处 合成事件&#xff08;SyntheticEvent&#xff09; React 使用合…

单片机之基本元器件的工作原理

一、二极管 二极管的工作原理 二极管是一种由P型半导体和N型半导体结合形成的PN结器件&#xff0c;具有单向导电性。 1. PN结形成 P型半导体&#xff1a;掺入三价元素&#xff0c;形成空穴作为多数载流子。N型半导体&#xff1a;掺入五价元素&#xff0c;形成自由电子作为多…

CNN 卷积神经网络处理图片任务 | PyTorch 深度学习实战

前一篇文章&#xff0c;学习率调整策略 | PyTorch 深度学习实战 本系列文章 GitHub Repo: https://github.com/hailiang-wang/pytorch-get-started CNN 卷积神经网络 CNN什么是卷积工作原理深度学习的卷积运算提取特征不同特征核的效果比较卷积核感受野共享权重池化 示例源码 …

3.1 学习UVM中的uvm_component类分为几步?

文章目录 前言一、定义1.1 角色和功能&#xff1a;1.2 与其他UVM类的区别&#xff1a;1.3 主要属性和方法&#xff1a; 二、使用方法2.1 定义和实例化&#xff1a;2.2 生命周期管理&#xff1a;2.3 组件间通信&#xff1a; 三、何时使用3.1 使用场景3.2 适用组件3.3 与uvm_obje…

谷云科技RestCloud全面接入DeepSeek 开启智能新时代

在数字化转型的浪潮中&#xff0c;谷云科技始终走在数据集成与智能应用领域的前沿。近期&#xff0c;随着 DeepSeek 的火爆出圈&#xff0c;谷云科技紧跟技术趋势&#xff0c;对旗下两大核心产品 —— 数据集成软件 ETLCloud 和 AI Agent 智能体构建平台进行了重大升级&#xf…

Kafka 入门与实战

一、Kafka 基础 1.1 创建topic kafka-topics.bat --bootstrap-server localhost:9092 --topic test --create 1.2 查看消费者偏移量位置 kafka-consumer-groups.bat --bootstrap-server localhost:9092 --describe --group test 1.3 消息的生产与发送 #生产者 kafka-cons…

FFmpeg 与 FFplay 参数详解:-f、-pix_fmt、-pixel_format 和 -video_size 的区别与用法

FFmpeg 与 FFplay 参数详解:-f、-pix_fmt、-pixel_format 和 -video_size 的区别与用法 在使用 FFmpeg 和 FFplay 进行视频处理和播放时,-f、-pix_fmt、-pixel_format 和 -video_size 是常用的参数。这些参数的作用和使用场景略有不同,理解它们的区别和用法对于正确处理和播…

即时通讯开源项目OpenIM配置离线推送全攻略

如何进行二次开发 如果您需要基于 OpenIM 开发新特性&#xff0c;首先要确定是针对业务侧还是即时通讯核心逻辑。 由于 OpenIM 系统本身已经做好了比较多的抽象&#xff0c;大部分聊天的功能已经具备了&#xff0c;不建议修改 IM 本身。 如果需要增加 IM 的能力&#xff0c;可以…

深度解析:网站快速收录与网站内容更新频率的关系

本文转自&#xff1a;百万收录网 原文链接&#xff1a;https://www.baiwanshoulu.com/97.html 网站快速收录与网站内容更新频率之间存在着密切的关系。以下是对这一关系的深度解析&#xff1a; 一、内容更新频率对网站快速收录的影响 提高收录速度 定期发布新内容会促使搜索…