【Java中序列化的原理是什么(解析)】

在这里插入图片描述

🍁序列化的原理是什么?

  • 🍁典型-----解析
  • 🍁拓展知识仓
    • 🍁Serializable 和 Externalizable 接门有何不同?
  • 🍁如果序列化后的文件或者原始类被篡改,还能被反序列化吗?
    • 🍁serialVersionUID 有何用途? 如果没定义会有什么问题?
  • 🍁在Java中,有哪些好的序列化框架,有什么好处?


🍁典型-----解析


序列化是将对象转换为可传输格式的过程。是一种数据的持久化手段。一般广泛应用于网络传输,RMI和RPC等场景中。 几乎所有的商用编程语言都有序列化的能力,不管是数据存储到硬盘,还是通过网络的微服务传输,都需要序列化能力。


在Java的序列化机制中,如果是String枚举或者实现了Serializable接口的类,均可以通过Java的序列化机制将类序列化为符合编码的数据流,然后通过InputStream和OutputStream将内存中的类持久化到硬盘或者网络中;同时,也可以通过反序列化机制将磁盘中的字节码再转换成内存中的类。


如果一个类想被序列化,需要实现Serializable接口。否则将抛出NotSerializableException异常。Serializable接门没有方法或字段,仅用于标识可序列化的语义。


自定义类通过实现Serializable接口做标识,进而在10中实现序列化和反序列化,具体的执行路径如下:


#write0bject -> #writeobjecto(判断类是否是自定义类) -> writeOrdinary0bject(区分Serializable和Externalizable) -> writeSerialData(序列化fields) -> invokewriteobject(反射调用类自己的序列化策略)


其中,在invokeWriteObject的阶段,系统就会处理自定义类的序列化方案。


这是因为,在序列化操作过程中会对类型进行检查,要求被序列化的类必须属于Enum、Array和Serializable类型其中的任何一种。


看一段代码,对象的序列化和反序列化:


import java.io.*;  
import java.util.*;  class Employee implements Serializable {  private String name;  private int age;  private Department department;  public Employee(String name, int age, Department department) {  this.name = name;  this.age = age;  this.department = department;  }  public String toString() {  return "Employee [name=" + name + ", age=" + age + ", department=" + department + "]";  }  
}  class Department implements Serializable {  private String name;  private List<Employee> employees;  public Department(String name) {  this.name = name;  this.employees = new ArrayList<>();  }  public void addEmployee(Employee employee) {  employees.add(employee);  }  public String toString() {  return "Department [name=" + name + ", employees=" + employees + "]";  }  
}  public class ComplexSerializationDemo {  public static void main(String[] args) throws IOException, ClassNotFoundException {  // 创建对象关系图  Department department1 = new Department("HR");  Department department2 = new Department("IT");  Employee employee1 = new Employee("Alice", 25, department1);  Employee employee2 = new Employee("Bob", 30, department2);  Employee employee3 = new Employee("Charlie", 35, department1);  department1.addEmployee(employee1);  department1.addEmployee(employee3);  department2.addEmployee(employee2);  // 序列化对象关系图到文件  FileOutputStream fileOut = new FileOutputStream("complexObject.ser");  ObjectOutputStream out = new ObjectOutputStream(fileOut);  out.writeObject(department1);  out.writeObject(department2);  out.writeObject(employee1);  out.writeObject(employee2);  out.writeObject(employee3);  out.close();  fileOut.close();  System.out.println("对象关系图已序列化到文件complexObject.ser");  // 从文件中反序列化对象关系图  FileInputStream fileIn = new FileInputStream("complexObject.ser");  ObjectInputStream in = new ObjectInputStream(fileIn);  Department department1_ = (Department) in.readObject();  Department department2_ = (Department) in.readObject();  Employee employee1_ = (Employee) in.readObject();  Employee employee2_ = (Employee) in.readObject();  Employee employee3_ = (Employee) in.readObject();  in.close();  fileIn.close();  System.out.println("从文件complexObject.ser反序列化的对象关系图:");  System.out.println("Department 1: " + department1_);  System.out.println("Department 2: " + department2_);  System.out.println("Employee 1: " + employee1_);  System.out.println("Employee 2: " + employee2_);  System.out.println("Employee 3: " + employee3_);  }  
}

🍁拓展知识仓


🍁Serializable 和 Externalizable 接门有何不同?


类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。


当试图对一个对象进行序列化的时候,如果遇到不支持 Serializable 接口的对象。在此情况下,将抛出NotSerializableException。


如果要序列化的类有父类,要想同时将在父类中定义过的变量持久化下来,那么父类也应该实现Java.io.Serializable接口。


Externalizable继承了Serializable,该接口中定义了两个抽象方法: writeExternal()与readExternal()。当使用Externalizable接口来进行序列化与反序列化的时候需要开发人员重写writeExternal()与readExternal()方法,如果没有在这两个方法中定义序列化实现细节,那么序列化之后,对象内容为空。实现Externalizable接口的类必须要提供一个public的无参的构造器。


所以,实现Externalizable,并实现writeExternal0和readExternal()方法可以指定序列化哪些属性。


🍁如果序列化后的文件或者原始类被篡改,还能被反序列化吗?


🍁serialVersionUID 有何用途? 如果没定义会有什么问题?


序列化是将对象的状态信息转换为可存储或传输的形式的过程。我们都知道,Java对象是保存在JVM的堆内存中的,也就是说,如果JVM堆不存在了,那么对象也就跟着消失了。


而序列化提供了一种方案,可以让你在即使JVM停机的情况下也能把对象保存下来的方案。就像我们平时用的U盘一样。


把Java对象序列化成可存诸或传输的形式(如二进制流),比如保存在文件中。这样,当再次需要这人对象的时候,从文件中读取出二进制流,再从二进制流中反序列化出对象。


但是,虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化ID 是否 致,即serialVersionUID要求 一致。


在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。这样做是为了保证安全,因为文件存储中的内容可能被篡改。


当实现iava.io.Serializable接口的类没有显式地定义一个serialVersionUID变量时候,Java序列化机制会根据编译的Class自动生成一个serialVersionUID作序列化版本比较用,这种情况下,如果Class文件没有发生变化,就算重偏译多次,serialVersionUID也不会变化的。但是,如果发生了变化,那么这个文件对应的serialVersionUID也就会发生变化。


基于以上原理,如果我们一个类实现了Serializable接口,但是没有定义serialVersionUID,然后序列化,在序列化之后,由于某些原因,我们对该类做了变更,重新启动应用后,我们相对之前序列化过的对象进行反序列化的话就会报错。


看一段代码,如何使用自定义的序列化方法,以及如何处理序列化过程中的异常:


import java.io.*;  
import java.util.*;  class Employee implements Serializable {  private static final long serialVersionUID = 1L;  private String name;  private int age;  private Set<String> skills;  public Employee(String name, int age, Set<String> skills) {  this.name = name;  this.age = age;  this.skills = skills;  }  public void displayInfo() {  System.out.println("Name: " + name);  System.out.println("Age: " + age);  System.out.println("Skills: " + skills);  }  // 自定义的序列化方法  private void writeObject(ObjectOutputStream out) throws IOException {  out.writeUTF(name);  out.writeInt(age);  out.writeObject(skills);  }  // 自定义的反序列化方法  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {  name = in.readUTF();  age = in.readInt();  skills = (Set<String>) in.readObject();  }  
}  public class SerializationDemo {  public static void main(String[] args) {  try {  // 创建一个 Employee 对象并序列化  Set<String> skills = new HashSet<>();  skills.add("Java");  skills.add("Python");  Employee employee = new Employee("John", 25, skills);  ByteArrayOutputStream baos = new ByteArrayOutputStream();  ObjectOutputStream oos = new ObjectOutputStream(baos);  employee.writeObject(oos);  // 使用自定义的序列化方法  oos.close();  // 反序列化 Employee 对象(确保使用相同的 serialVersionUID)  ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());  ObjectInputStream ois = new ObjectInputStream(bais);  Employee deserializedEmployee = new Employee("", 0, new HashSet<>());  // 创建一个新的 Employee 对象用于反序列化  deserializedEmployee.readObject(ois);  // 使用自定义的反序列化方法  ois.close();  deserializedEmployee.displayInfo();  // 输出员工的详细信息  } catch (IOException e) {  e.printStackTrace();  } catch (ClassNotFoundException e) {  e.printStackTrace();  }  }  
}

在上面的示例中,为 Employee 类实现了 writeObject 和 readObject 方法,以自定义序列化和反序列化的过程。我们在 writeObject 方法中使用了 ObjectOutputStream 的 writeUTF、writeInt 和 writeObject 方法来写入员工的姓名、年龄和技能集合。在 readObject 方法中,我们使用 ObjectInputStream 的 readUTF、readInt 和 readObject 方法来读取这些值。我们还创建了一个新的 Employee 对象用于反序列化,并调用了自定义的反序列化方法。这个示例展示了如何处理序列化和反序列化过程中的异常,并展示了如何使用自定义的序列化方法来控制对象的序列化和反序列化过程。


🍁在Java中,有哪些好的序列化框架,有什么好处?


Java中常用的序列化框架:


java、 kryo、hessian、 protostuff、 gson、fastjson等。


Kryo: 速度快,序列化后体积小: 跨语言支持较复杂


Hessian: 默认支持跨语言: 效率不高


Protostuff: 速度快,基于protobuf; 需静态编译


Protostuff-Runtime: 无需静态编译,但序列化前需预先传入schema; 不支持无默认构造函数的类,反序列化时需用户自己初始化序列化后的对象,其只负责将该对象进行赋值


Java: 使用方便,可序列化所有类;速度慢,占空间

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

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

相关文章

【SpringCloud笔记】(9)分布式配置中心之Config

Config 概述 分布式系统当前面临的配置问题 微服务意味着要将单体应用中的业务拆分成一个个子服务&#xff0c;每个服务的粒度相对较小&#xff0c;因此系统中会出现大量的服务。 比如&#xff1a;有n个微服务连接同一套数据库&#xff0c;当连接数据库需要发生变动时&…

Exploring the Limits of Masked Visual Representation Learning at Scale论文笔记

论文名称&#xff1a;EVA: Exploring the Limits of Masked Visual Representation Learning at Scale 发表时间&#xff1a;CVPR2023 作者及组织&#xff1a;北京人工智能研究院&#xff1b;华中科技大学&#xff1b;浙江大学&#xff1b;北京理工大学 GitHub&#xff1a;http…

ARM串口通信编程实验

完成&#xff1a;从终端输入选项&#xff0c;完成点灯关灯&#xff0c;打开风扇关闭风扇等操作 #include "gpio.h" int main() {char a;//char buf[128];uart4_config();gpio_config();while(1){//接收一个字符数据a getchar();//发送接收的字符putchar(a);switch(…

redis复习笔记01(小滴课堂)

高并发的必备两大“核技术”队列和缓存 介绍本地缓存和分布式缓存 Nosql介绍和Reidis介绍 Linux服务器源码安装Redis6和相关依赖 在路径下上传压缩包。 上传压缩包。 版本更新了&#xff0c;但这是临时的。 版本更新了。 解压压缩包&#xff1a; 重命名&#xff1a; 我们可以看…

C# WPF上位机开发(MySql访问)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们学习了数据库sqlite&#xff0c;不过这是一种小型的数据库&#xff0c;作为内部使用还可以。但是&#xff0c;如果要与外面的其他供应商进…

蓝牙物联网在智慧医疗中的应用

物联网技术开启了万物互联的时代&#xff0c;并且随着智慧城市建设的加速推进及物联网技术对各行业的逐步渗透&#xff0c;“智慧”概念应运而生&#xff0c;诸如智慧能源、智慧交通、智慧医疗等“遍地开花”&#xff0c;可以说&#xff0c;物联网技术给各行业带来了产业模式上…

Arduino/Android 蓝牙通信系统设计解决方案

随着当今安全管理的发展需求以及国家对安全监控行业的支持&#xff0c;这几年&#xff0c;安全监控行业发展迅猛&#xff0c;各类监控系统百花齐放。传统的温度监控系统通过有线或其他方式传送温度数据&#xff0c;而本文提出了利用蓝牙无线传输数据的设计方案&#xff0c;这种…

FLStudio21中文版水果编曲软件好用吗?如何下载最新版本

FL Studio21版是一款在国内非常受欢迎的多功能音频处理软件&#xff0c;我们可以通过这款软件来对多种不同格式的音频文件来进行编辑处理。而且FL Studio 21版还为用户们准备了超多的音乐乐器伴奏&#xff0c;我们可以直接一键调取自己需要的音调。 FL Studio21版不仅拥有非常…

金和OA C6 gethomeinfo sql注入漏洞

产品介绍 金和网络是专业信息化服务商,为城市监管部门提供了互联网监管解决方案,为企事业单位提供组织协同OA系统开发平台,电子政务一体化平台,智慧电商平台等服务。 漏洞概述 金和 OA C6 gethomeinfo接口处存在SQL注入漏洞&#xff0c;攻击者除了可以利用 SQL 注入漏洞获取…

vue3+ts pinia存储及持久化

index.ts 需要安装pinia-plugin-persist npm i pinia-plugin-persist -Simport { createPinia} from "pinia" // 引入批量的pinia持久存储插件 import piniaPluginPersist from pinia-plugin-persist const storecreatePinia(); store.use(piniaPluginPers…

【网络安全 | 网络协议】结合Wireshark讲解TCP三次握手

TCP三次握手在Wireshark数据包中是如何体现的&#xff1f;在此之前&#xff0c;先熟悉TCP三次握手的流程。 TCP三次握手流程 TCP&#xff08;传输控制协议&#xff09;是一种面向连接的、可靠的传输层协议。在建立 TCP 连接时&#xff0c;需要进行三次握手&#xff0c;防止因为…

【自然语言处理】用Python从文本中删除个人信息-第二部分

自我介绍 做一个简单介绍&#xff0c;酒架年近48 &#xff0c;有20多年IT工作经历&#xff0c;目前在一家500强做企业架构&#xff0e;因为工作需要&#xff0c;另外也因为兴趣涉猎比较广&#xff0c;为了自己学习建立了三个博客&#xff0c;分别是【全球IT瞭望】&#xff0c;【…

uni-app pages.json之globalStyle全局页面样式配置

锋哥原创的uni-app视频教程&#xff1a; 2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中..._哔哩哔哩_bilibili2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中...共计23条视频&#xff0c;包括&#xff1a;第1讲 uni…

STM32软硬件CRC测速对比

硬件CRC配置 以及软硬件CRC速度对比 使用CUBEMX配置默认使用的是CRC32&#xff0c;从库中可以看出这一点 HAL库提供了以下两个计算函数 HAL_CRC_Accumulate(CRC_HandleTypeDef *hcrc, uint32_t pBuffer[], uint32_t BufferLength); 这个函数用于在已有的CRC校验结果的基础上累积…

听GPT 讲Rust源代码--src/tools(26)

File: rust/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs 在Rust源代码中&#xff0c;iter_out_of_bounds.rs文件是Clippy lints库的一部分&#xff0c;该库用于静态代码分析&#xff0c;用于检测Rust代码中的潜在问题和错误。iter_out_of_bounds.rs文件中…

第三天:对ThreadLocal理解

ThreadLocal是什么&#xff1f; ThreadLocal&#xff0c;也就是线程本地变量。如果你创建了一个 ThreadLocal变量&#xff0c;那么访问这个变量的每个线程都会有这个变量的一个本地副本&#xff0c;多个线程操作这个变量的时候&#xff0c;实际是操作自己本地内存里面的变量&a…

5G NTN与“手机直连卫星”快速原型

5G非地面网络(5G NTN) 5G非地面网络(Non-Terrestrial Network, NTN)是一项旨在使5G用户终端(5G UE)连接到 位于卫星上的非地面基站(5G gNB)的技术NTN是3GPP R17版本的重要功能&#xff0c;在5G-Advanced中持续演进&#xff0c;已成为3GPP Release 18 工作计划中的重要组成部分…

Wi-Fi、蓝牙、ZigBee等多类型无线连接方式的安全物联网网关设计

随着物联网和云计算技术的飞速发展.物联网终端的数量越来越多&#xff0c;终端的连接方式也更趋多样化&#xff0c;比如 Wi-Fi蓝牙和 ZigBee 等。现有的物联网网关大多仅支持一种或者几种终端的接人方式。无法满足终端异构性的需求。同时&#xff0c;现有的物联网网关与终端设备…

php 不加后缀访问

实现不带后缀访问php文件的方法&#xff1a;首先在htaccess文件中加入内容“RewriteRule ^(api/token) token.php [L]”&#xff1b;然后通过根目录下的“token.php”来接受“api/token”&#xff1b;最后修改配置文件。 考虑的做法有&#xff1a; HTTP重写技术&#xff0c;让…

MySQL数据库MySQL事务与存储引擎

目录 一、MySQL事务 &#xff08;一&#xff09;定义 &#xff08;二&#xff09;ACID特性 1.原子性 2.一致性 3.隔离性 4.持久性 &#xff08;三&#xff09;隔离级别 1.可能出现的一致性问题 &#xff08;1&#xff09;脏读 &#xff08;2&#xff09;不可重复读 …