【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.rhkb.cn/news/14848.html

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

相关文章

android apk反编译

使用解压工具解压apk&#xff0c;使用dex2jar解析其中的dex文件为jar文件&#xff0c;再使用jd-gui将class反编译为java文件 1 工具下载 dex2jar下载 https://github.com/pxb1988/dex2jar/releases 直接去github上下载最新发布版本 注意&#xff0c;如果后面使用过程中遇到No…

PAT乙级( 1009 说反话 1010 一元多项式求导)C语言版本超详细解析

1009 说反话 给定一句英语&#xff0c;要求你编写程序&#xff0c;将句中所有单词的顺序颠倒输出。 输入格式&#xff1a; 测试输入包含一个测试用例&#xff0c;在一行内给出总长度不超过 80的字符串。字符串由若干单词和若干空格组成&#xff0c;其中单词是由英文字母&#x…

C++20导出模块及使用

1.模块声明 .ixx文件为导入模块文件 math_operations.ixx export module math_operations;//模块导出 //导出命名空间 export namespace math_ {//导出命名空间中函数int add(int a, int b);int sub(int a, int b);int mul(int a, int b);int div(int a, int b); } .cppm文件…

鸿蒙接入支付宝SDK后模拟器无法运行,报错error: install parse native so failed.

鸿蒙项目接入支付宝后&#xff0c;运行提示error: install parse native so failed. 该问题可能由于设备支持的 Abi 类型与 C 工程中的不匹配导致. 官网error: install parse native so failed.错误解决办法 根据官网提示在模块build-profile.json5中添加“x86_64”依然报错 问…

MongoDB开发规范

分级名称定义P0核心系统需7*24不间断运行&#xff0c;一旦发生不可用&#xff0c;会直接影响核心业务的连续性&#xff0c;或影响公司名誉、品牌、集团战略、营销计划等&#xff0c;可能会造成P0-P2级事故发生。P1次核心系统这些系统降级或不可用&#xff0c;会间接影响用户使用…

nodejs - vue 视频切片上传,本地正常,线上环境导致磁盘爆满bug

nodejs 视频切片上传&#xff0c;本地正常&#xff0c;线上环境导致磁盘爆满bug 原因&#xff1a; 然后在每隔一分钟执行du -sh ls &#xff0c;发现文件变得越来越大&#xff0c;即文件下的mp4文件越来越大 最后导致磁盘直接爆满 排查原因 1、尝试将m3u8文件夹下的所有视…

微信小程序如何使用decimal计算金额

第三方库地址&#xff1a;GitHub - MikeMcl/decimal.js: An arbitrary-precision Decimal type for JavaScript 之前都是api接口走后端计算&#xff0c;偶尔发现这个库也不错&#xff0c;计算简单&#xff0c;目前发现比较准确 上代码 导入js import Decimal from ../../uti…

unity学习29:摄像机camera相关skybox 和 Render Texture测试效果

目录 1 摄像机 1.1 每个Scene里都自带一个摄像机 camera 1.2 可以创建多个camera 1.3 下面先看backgroundtype: 2 backgroundtype: 天空盒 skybox 2.1 清除标志,清除&#xff1a;天空盒 自选天空盒 2.2 window /Asset Store 2.3 导入skybox 3 backgroundtype: 纯色…

ximalaya(三) playUriList值解密--webpack

本文主要介绍解密音频播放url参数。 本文仅代表个人理解&#xff0c;如有其他建议可在评论区沟通。 声明 仅仅记录一下自己的学习方法&#xff0c;不作为其他参考、更不作为商业用途。如有侵犯请联系本人删除 目标地址&#xff1a;aHR0cHM6Ly93d3cueGltYWxheWEuY29tL3NvdW5k…

C# Winform怎么设计串口,客户端和相机控件界面显示

首先我们必须把这个类创建好 INIAPI using System; using System.Text; using System.Runtime.InteropServices;namespace Ini {public class IniAPI{#region INI文件操作/** 针对INI文件的API操作方法&#xff0c;其中的节点&#xff08;Section)、键&#xff08;KEY&#x…

【redis】数据类型之hash

Redis中的Hash数据类型是一种用于存储键值对集合的数据结构。与Redis的String类型不同&#xff0c;Hash类型允许你将多个字段&#xff08;field&#xff09;和值&#xff08;value&#xff09;存储在一个单独的key下&#xff0c;从而避免了将多个相关数据存储为多个独立的key。…

Qt:Qt Creator项目创建

目录 认识Qt Creator Qt Creator概览 使用Qt Creator新建项目 选择项目模板 选择项目路径 选择构建系统 填写类信息设置界面 选择语言和翻译文件 选择Qt套件 选择版本控制系统 最终效果 认识Qt Creator Qt Creator概览 从开始菜单或者快捷方式打开Qt Creator集成开…

家用报警器的UML 设计及其在C++和VxWorks 上的实现01

M.W.Richardson 著&#xff0c;liuweiw 译 论文描述了如何运用 UML&#xff08;统一建模语言&#xff09;设计一个简单的家用报警器&#xff0c;并实现到 VxWorks 操作系统上。本文分两个部分&#xff0c;第一部分描述了如何用 UML 设计和验证家用报警器的模型&#xff0c;以使…

node.js + html + Sealos容器云 搭建简易多人实时聊天室demo 带源码

node.js html Sealos容器云 搭建简易多人实时聊天室demo 带源码 前言功能介绍&#xff08;demo演示&#xff09;sealos官网配置node.js 编写服务端代码前端ui 调用接口整体项目目录部署到服务器 前言 hello哦盆友们&#xff0c;这次我们来十几行代码做一个超简单的多人聊天…

CPP集群聊天服务器开发实践(一):用户注册与登录

目录 1 客户端用户注册与登录 1.1 主要思想 1.2 网络层 1.3 业务层 1.4 数据层 1.5 测试结果 1 客户端用户注册与登录 1.1 主要思想 实现网络层、业务层、数据层的解耦&#xff0c;提高系统的可维护性。 网络层&#xff1a;主要实现对客户端连接、客户端读写请求的捕获…

【C语言】数 组与指针:深度剖析与等价表达

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C语言 文章目录 &#x1f4af;前言&#x1f4af;数组与指针的基本关系&#x1f4af;数组与指针的互换使用数组下标与指针的等价性 &#x1f4af;六个表达式的等价性&#x1f4af;指针运算的注意事项&#x1f4af;数组…

寒假2.7

题解 web&#xff1a;[HCTF 2018]WarmUp 打开是张表情包 看一下源代码 访问source.php&#xff0c;得到完整代码 代码审计 <?phphighlight_file(__FILE__);class emmm{public static function checkFile(&$page){$whitelist ["source">"source.p…

sqli-lab靶场学习(五)——Less15-17(post方法盲注、修改密码)

前言 第11-14关开始用post方法&#xff0c;15-17关会用到盲注&#xff0c;post方法盲注和get方法类似。 Less15 这关是单引号闭合&#xff0c;有报错但没有具体情况的回显&#xff0c;因此适合使用错误盲注。 在用户名密码框分别输入 账号&#xff1a;admin and 11 -- asd…

【Spring】什么是Spring?

什么是Spring&#xff1f; Spring是一个开源的轻量级框架&#xff0c;是为了简化企业级开发而设计的。我们通常讲的Spring一般指的是Spring Framework。Spring的核心是控制反转(IoC-Inversion of Control)和面向切面编程(AOP-Aspect-Oriented Programming)。这些功能使得开发者…

VSCode便捷开发

一、常用插件 Vue 3 Snippets、Vetur、Vue - Official 二、常用开发者工具 三、Vue中使用Element-UI 安装步骤&#xff1a; 1、在VSCode的终端执行如下指令&#xff1a; npm i element-ui -S 2、在main.js中全局引入&#xff1a; import Vue from vue; import ElementUI from …