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

news2024/9/23 1:20:43

在这里插入图片描述

🍁序列化的原理是什么?

  • 🍁典型-----解析
  • 🍁拓展知识仓
    • 🍁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.coloradmin.cn/o/1335620.html

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

相关文章

电路设计(7)——窗口比较器的multism仿真

1.功能设计 构建一个窗口比较器的电路&#xff0c;在输入电压大于3.5v&#xff0c;小于0.8v时&#xff0c;蜂鸣器报警&#xff0c;输入电压在0.8v到3.5v之间时&#xff0c;不报警。 整体电路如下&#xff1a; 2.设计思路 在输入端&#xff0c;采取电阻分压的方式&#xff0c;输…

2024年最新Python爬虫入门『最强教程』新鲜出炉!

近年来&#xff0c;大数据成为业界与学术界最火热的话题之一&#xff0c;数据已经成为每个公司极为重要的资产。互联网大量的公开数据为个人和公司提供了以往想象不到的可以获取的数据量。而掌握网络爬虫技术可以帮助你获取这些有用的公开数据集。 爬虫能干什么呢&#xff1f;一…

11-GraalVM元原生时代的Java虚拟机

文章目录 GraalVM诞生的背景Java在微服务/云原生时代的困境事实矛盾 问题根源Java离不开虚拟机 解决方案革命派保守派 GraalVM入门GraalVM特征GraalVM下载和安装GraalVM下载win10安装及配置linux安装及配置 GraalVM初体验(Linux)多语言开发(了解即可、官网有Demo)GraalCompiler…

无人叉车驻车定位RFID传感器CNS-RFID-01|1S的CAN总线通信连接方法

无人叉车驻车定位RFID传感器CNS-RFID-01|1S支持CAN总线通信方式&#xff0c;广泛应用于智能仓库&#xff0c;AGV |RGV小车&#xff0c;无人叉车&#xff0c;搬运机器人定位&#xff0c;驻车等领域&#xff0c;本篇幅主要介绍器CNS-RFID-01|1S RFID传感器的CAN总线通信连接方法。…

“双十一、二” 业务高峰如何扛住?韵达快递选择 TDengine

小 T 导读&#xff1a; 为了有效处理每日亿级的数据量&#xff0c;早在 2021 年&#xff0c;韵达就选择用 TDengine 替代了 MySQL&#xff0c;并在三台服务器上成功部署和上线了 TDengine 2.0 集群。如今&#xff0c;随着 TDengine 3.0 版本的逐渐成熟&#xff0c;韵达决定将现…

NAT协议的实现方式

在网络通信中&#xff0c;NAT协议&#xff08;Network Address Translation&#xff0c;网络地址转换&#xff09;扮演着关键角色&#xff0c;允许内部网络与外部网络之间进行有效的通信。 实现内外网之间网络地址转换的过程中&#xff0c;NAT采用了不同的实现方式&#xff0c;…

案例分析:三一重工集团数字化转型

三一重工集团&#xff0c;作为制造业中的数字化转型佼佼者&#xff0c;荣获“全球灯塔工厂”的殊荣&#xff0c;率先采用了物联网、云计算、大数据等尖端技术手段。数字化转型让三一重工步入了全面信息化的管理时代&#xff0c;通过ERP、CRM、HRM等系统的协同运作&#xff0c;实…

高度可定制的JS电子表格组件DHTMLX Spreadsheet v5.1——拥有全新内置主题

DHTMLX Spreadsheet是用纯JavaScript编写的开源电子表格小部件&#xff0c;可让您快速在网页上添加类似于Excel的可编辑数据表。高度可定制的JavaScript电子表格组件&#xff0c;具有优雅的Material样式&#xff0c;可安全、方便地编辑和格式化数据。 近日DHTMLX Spreadsheet …

HashMap扩展问题:HashMap如何实现线程安全?

HashMap如何实现线程安全&#xff1f; 方法一&#xff1a;java.util.Collections.synchronizedMap(Map<K,V> m) 底层实际上是将hashMap又封装了一层&#xff0c;变成SynchronizedMap<K,V>&#xff0c;并在每一个对HashMap的操作方法上添加了synchronized修饰。代…

基于 Webpack 插件体系的 Mock 服务

背景 在软件研发流程中&#xff0c;对于前后端分离的架构体系而言&#xff0c;为了能够更快速、高效的实现功能的开发&#xff0c;研发团队通常来说会在产品原型阶段对前后端联调的数据接口进行结构设计及约定&#xff0c;进而可以分别同步进行对应功能的实现&#xff0c;提升研…

WooCommerce Cost of Goods电商商城商品成本插件 轻松跟踪利润

WooCommerce Cost of Goods电商商城商品成本插件 轻松跟踪利润 WooCommerce Cost of Goods电商商城商品成本插件通过将货物成本纳入订单和报告中&#xff0c;轻松跟踪利润。 WooCommerce Cost of Goods电商商城商品成本插件功能 WooCommerce Cost of Goods电商商城商品成本插…

设计模式--工厂方法模式

实验3&#xff1a;工厂方法模式 本次实验属于模仿型实验&#xff0c;通过本次实验学生将掌握以下内容&#xff1a; 1、理解工厂方法模式的动机&#xff0c;掌握该模式的结构&#xff1b; 2、能够利用工厂方法模式解决实际问题。 [实验任务]&#xff1a;加密算法 目前常用…

IntelliJ IDEA插件

插件安装目录&#xff1a;C:\Users\<username>\AppData\Roaming\JetBrains\IntelliJIdea2021.2\plugins aiXcoder Code Completer&#xff1a;代码补全 Bookmark-X&#xff1a;书签分类 使用方法&#xff1a;鼠标移动到某一行&#xff0c;按ALT SHIFT D

静态HTTP:构建高效、可扩展的Web应用程序的基础

静态HTTP是Web应用程序的重要组成部分&#xff0c;它为构建高效、可扩展的Web应用程序提供了坚实的基础。下面将详细介绍静态HTTP的优势和在Web应用程序中的作用。 一、静态HTTP的优势 高效性能&#xff1a;静态HTTP内容在服务器上预先生成&#xff0c;然后通过HTTP协议传输到…

STM32MP157D-DK1开发板Qt镜像构建

上篇介绍了STM32MP57-DK1开发板官方系统的烧录。那个系统包含Linux系统的基础功能&#xff0c;如果要进行Qt开发&#xff0c;还需要重新构建带有Qt功能的镜像 本篇就来介绍如何构建带有Qt功能的系统镜像&#xff0c;并在开发板中烧录构建的镜像。 1 Distribution包的构建 ST…

Unity 如何获取当前日期的中文星期几

要获取当前日期是星期几可以使用DateTime下的DayOfWeek方法。 首先我们在脚本中添加System引用&#xff1a; using System; 然后我们再调用DateTime下的DayOfWeek方法&#xff1a; DayOfWeek dayOfWeek DateTime.Now.DayOfWeek; //获取当前是星期几 由于返回的是英文&…

simulink代码生成(三)——自定义变量名称

在simulink代码生成的学习过程中&#xff0c;遇到了一个卡壳的问题&#xff1a;如何在生成的代码中定义一个可控变量&#xff1f; 给大家看一下原m代码与生成的C代码对比结果&#xff1a; 原来的m函数代码&#xff1a;结构清晰&#xff0c;变量名与物理意义对应 生成的代码&a…

详解Java反射机制reflect(一学就会,通俗易懂)

1.定义 #2. 获取Class对象的三种方式 sout(c1)结果为class com.itheima.d2_reflect.TestClass 获取到了Class对象就相当于获取到了该类 2.获取类的构造器 3.获取全部构造器对象 2.根据参数类型获取构造器对象 类型后必须加.class 3.构造器对象调用构造器方法 4.暴力访问 4.获…

opencv入门到精通——图像平滑

目录 目标 2D卷积&#xff08;图像过滤&#xff09; 图像模糊&#xff08;图像平滑&#xff09; 1.平均 2.高斯模糊 3.中位模糊 4.双边滤波 目标 学会&#xff1a; 使用各种低通滤镜模糊图像 将定制的滤镜应用于图像&#xff08;2D卷积&#xff09; 2D卷积&#xff0…

【JavaScript】FileReader读取文件成功,但存储的数据为空——总结

目录 问题解决 问题 如题&#xff0c;使用下列代码读取上传的文件&#xff1a; for (let i 0; i < files.length; i) {const reader new FileReader();const fileName files[i].name;reader.onload function(e) {file_datas[fileName] e.target.result;}// 根据需要…