【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 final
    private 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);

        // 尝试修改原始 hobbies
        hobbies.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方法,即使有修改的需求也是返回一个新对象。

在这里插入图片描述

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

upx压缩工具使用说明

UPX&#xff08;Ultimate Packer for Executables&#xff09;是一款开源的可执行文件打包工具&#xff0c;能够将可执行文件&#xff08;如Windows的.exe文件或Linux的ELF文件&#xff09;进行压缩&#xff0c;以减少文件大小&#xff0c;并增加反逆向工程的难度。 下载相关安…

DeepSeek-R1 32B Windows+docker本地部署

最近国产大模型DeepSeek兴起&#xff0c;本地部署了一套deepseek同时集成Open WebUI界面,给大家出一期教程。 软件&#xff1a;Ollama、docker、Open WebUI 一、用Ollama下载模型 首先我们需要安装Ollama&#xff0c;它可以在本地运行和管理大模型。 到Ollama官网 https://ol…

活动预告 |【Part 2】Microsoft 安全在线技术公开课:通过扩展检测和响应抵御威胁

课程介绍 通过 Microsoft Learn 免费参加 Microsoft 安全在线技术公开课&#xff0c;掌握创造新机遇所需的技能&#xff0c;加快对 Microsoft Cloud 技术的了解。参加我们举办的“通过扩展检测和响应抵御威胁”技术公开课活动&#xff0c;了解如何更好地在 Microsoft 365 Defen…

(2024|CVPR,MLLM 幻觉)OPERA:通过过度信任惩罚和回顾分配缓解多模态大型语言模型中的幻觉

OPERA: Alleviating Hallucination in Multi-Modal Large Language Models via Over-Trust Penalty and Retrospection-Allocation 目录 1. 引言 2. 相关研究 2.1 多模态大语言模型 2.2 LLM 的幻觉与解决方案 2.3. 语言模型中的解码策略 3. 方法 3.1 MLLM 生成过程 3.2…

激活函数篇 03 —— ReLU、LeakyReLU、ELU

本篇文章收录于专栏【机器学习】 以下是激活函数系列的相关的所有内容: 一文搞懂激活函数在神经网络中的关键作用 逻辑回归&#xff1a;Sigmoid函数在分类问题中的应用 整流线性单位函数&#xff08;Rectified Linear Unit, ReLU&#xff09;&#xff0c;又称修正线性单元&a…

C++20新特性

作者&#xff1a;billy 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 前言 C20 是 C 标准中的一个重要版本&#xff0c;引入了许多新特性和改进&#xff0c;包括模块&#xff08;Modules&#xff09;、协程…

新增md、html压缩文档上传,开放接口访问密钥改为多个,zyplayer-doc 2.4.7 发布啦!

zyplayer-doc是一款适合企业和个人使用的WIKI知识库管理工具&#xff0c;支持在线编辑富文本、Markdown、表格、Office文档、API接口、思维导图、Drawio以及任意的文本文件&#xff0c;专为私有化部署而设计&#xff0c;最大程度上保证企业或个人的数据安全&#xff0c;支持以内…

ES管理器焕新升级:紫色银狼主题来袭!

ES管理器&#xff08;安卓版&#xff09;迎来了一次令人眼前一亮的改头换面&#xff01;此次更新最直观的变化集中在UI界面设计上。开发团队大胆突破&#xff0c;摒弃了以往稍显平庸的风格&#xff0c;引入了极具个性的全新主题——以热门游戏《崩坏&#xff1a;星穹铁道》中的…

SwiftUI 学习 Toggle 遇到的问题

最近学习SwifyUI&#xff0c;心中存疑&#xff0c;于是记录这些问题 List {Toggle(isOn: $showFavoriteOnly) {Text("Favorite only")}ForEach(filterLandmarks) { landmark in// 在 NavigationLink 中&#xff0c;label 是用于指定导航链接显示内容的部分Navigati…

HarmonyOS:电话号码格式化

一、使用场景 不同国家和地区的电话号码在号码位数、组合方式、呈现方式等都存在差异。同时&#xff0c;在不同环境和条件下&#xff0c;电话号码可能存在不同的拨打方式和号码格式。例如&#xff0c;在中国境内跨地区打电话&#xff0c;通常需要先输入“0”&#xff0c;再拨打…

网络分析工具—WireShark的安装及使用

Wireshark 是一个广泛使用的网络协议分析工具&#xff0c;常被网络管理员、开发人员和安全专家用来捕获和分析网络数据包。它支持多种网络协议&#xff0c;能够帮助用户深入理解网络流量、诊断网络问题以及进行安全分析。 Wireshark 的主要功能 数据包捕获与分析&#xff1a; …

优惠券平台(十七):实现用户查询/取消优惠券预约提醒功能

业务背景 当用户预约了一个或多个优惠券抢购提醒后&#xff0c;如果不再需要提醒&#xff0c;可以取消预约通知。不过&#xff0c;虽然用户可以取消提醒&#xff0c;但已经发送到 MQ 的消息不会被撤回&#xff0c;消费者在时间点到达时依然会收到消息。此时&#xff0c;我们不…

10vue3实战-----实现登录的基本功能

10vue3实战-----实现登录的基本功能 1.基本页面的搭建2.账号登录的验证规则配置3.点击登录按钮4.表单的校验5.账号的登录逻辑和登录状态保存6.定义IAccount对象类型 1.基本页面的搭建 大概需要搭建成这样子的页面: 具体的搭建界面就不多讲。各个项目都有自己的登录界面&#…

盘姬工具箱:完全免费的电脑工具箱

今天给大家介绍一个非常好用的系统工具箱&#xff0c;里面内含100多个工具&#xff0c;完全免费使用&#xff0c;而且没有广告&#xff0c;非常的棒。 盘姬工具箱&#xff1a;完全免费的电脑工具箱 盘姬工具箱是一款完全免费的电脑工具箱&#xff0c;功能丰富且实用。软件下载并…

国产编辑器EverEdit - 编辑辅助功能介绍

1 编辑辅助功能 1.1 各编辑辅助选项说明 1.1.1 行号 打开该选项时&#xff0c;在编辑器主窗口左侧显示行号&#xff0c;如下图所示&#xff1a; 1.1.2 文档地图 打开该选项时&#xff0c;在编辑器主窗口右侧靠近垂直滚动条的地方显示代码的缩略图&#xff0c;如下图所示&…

【Golang学习之旅】Go + MySQL 数据库操作详解

文章目录 前言1. GORM简介2. 安装GORM并连接MySQL2.1 安装GORM和MySQL驱动2.2 连接MySQL 3. GORM数据模型&#xff08;Model&#xff09;3.1 定义User结构体3.2 自动迁移&#xff08;AutoMigrate&#xff09; 4. GORM CRUD 操作4.1 插入数据&#xff08;Create&#xff09;4.2 …

DeepSeek Janus Pro 论文解析

目录 介绍 统一的多模态理解与生成 图像理解任务 图像生成任务 统一模型的好处 Janus 和 Janus Pro 架构 Janus Pro主要设计原理 Janus Pro 图像编码器 LLM 处理和输出 Rectified Flow Janus Pro 训练流程 第一阶段——适应 第二阶段——统一预训练 第三阶段——监…

《Java核心技术 卷II》本地化的数字格式

数字格式 数字和货币的格式高度依赖locale。 格式化对象的集合&#xff0c;可以对java.text包中的数字进行格式化和解析。 格式化数字值 对特定locale的数字进行格式化的步骤&#xff1a; 得到Locale对象使用工厂方法得到一个格式器对象。使用这个格式器对象来完成格式化解析工…

四模型消融实验!DCS-CNN-BiLSTM-Attention系列四模型多变量时序预测

四模型消融实验&#xff01;DCS-CNN-BiLSTM-Attention系列四模型多变量时序预测 目录 四模型消融实验&#xff01;DCS-CNN-BiLSTM-Attention系列四模型多变量时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于DCS-CNN-BiLSTM-Attention、CNN-BiLSTM-Attention…

51单片机之引脚图(详解)

8051单片机引脚分类与功能笔记 1. 电源引脚 VCC&#xff08;第40脚&#xff09;&#xff1a;接入5V电源&#xff0c;为单片机提供工作电压。GND&#xff08;第20脚&#xff09;&#xff1a;接地端&#xff0c;确保电路的电位参考点。 2.时钟引脚 XTAL1&#xff08;第19脚&a…