一文解决序列化&反序列化体系

序列化&反序列化

1、什么是序列化&反序列化?

序列化(Serialization)

序列化是将对象的状态信息转换为可以存储或传输的形式的过程。在Java中,这通常意味着将一个对象(通常是一个实现了java.io.Serializable接口的类的实例)转换成一系列字节,这些字节可以被保存到文件中或通过网络发送到另一个位置。这样做的目的主要是实现两个目标:

  1. 持久化存储:通过序列化,对象可以被永久地保存到存储介质(如硬盘)上,即使程序结束运行,对象的状态也可以被保存下来,并在需要时重新加载。

  2. 网络传输:序列化后的字节流可以通过网络发送给其他计算机,实现对象在不同计算机之间的共享。

::: 总结:将数据结构或对象转换成二进制字节流的过程 :::

反序列化(Deserialization)

反序列化是序列化的逆过程,即将已经序列化成字节序列的对象重新转换成内存中的对象。在Java中,这通常意味着读取一个文件或网络流中的字节序列,并将它们重新构造为原始对象。反序列化的过程允许程序在需要时重新加载对象的状态,从而继续之前的操作或进行其他处理。

::: 总结:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程 :::

避免序列化某些属性

在Java中,如果对象的某些属性不希望被序列化,可以通过以下两种方式来实现:

  1. 使用static修饰:静态属性属于类,而不是类的任何特定实例。由于序列化操作是针对对象的实例进行的,因此静态属性不会被序列化。但请注意,这种方法并不是为了避免序列化而设计的,它主要是用于定义类级别的变量和方法。

  2. 使用transient关键字:transient关键字用于声明类的成员变量,以指示该成员变量不参与序列化过程。被transient修饰的成员变量在序列化时会被忽略,不会被写入到字节序列中,同样,在反序列化时,这些变量也不会被恢复。这是避免序列化特定属性的直接和常用的方法。

::: 注意:序列化只存在于堆区。 :::

2、序列化&反序列的使用场景

1. 网络通信

  • 数据传输:在网络通信中,经常需要将对象从一个系统发送到另一个系统。通过序列化,可以将对象转换为字节流,然后通过网络进行传输。接收方在收到字节流后,通过反序列化将其恢复为原始对象,从而实现数据的跨系统传输。这种机制在分布式系统、微服务架构以及客户端-服务器应用程序中非常常见。

2. 数据持久化

  • 对象存储:通过序列化,可以将对象的状态持久化到磁盘或数据库中。这样,当程序重新启动时,可以从存储介质中读取序列化后的对象,并恢复其状态。这对于需要长期保存对象状态的应用程序非常有用,如游戏、编辑器、数据库应用等。

  • 备份与恢复:序列化还可以用于数据备份和恢复。通过序列化整个系统或关键对象,可以创建数据的快照,以便在系统故障或数据丢失时进行恢复。

3. 分布式计算

  • 数据共享:在分布式计算环境中,多个计算节点需要共享数据。通过序列化,可以将对象转换为字节流,并在不同的计算节点之间传输。接收节点在收到字节流后,通过反序列化将其恢复为原始对象,从而实现数据的共享。

  • 远程方法调用(RPC):在RPC中,客户端将调用参数序列化为字节流,并发送给服务器。服务器在收到字节流后,通过反序列化将其恢复为原始参数,并执行相应的方法。然后,服务器将方法的返回值序列化并发送给客户端,客户端在收到返回值后通过反序列化恢复为原始对象。

4. 缓存存储

  • 提高性能:将对象序列化后存储在缓存中,可以减少对数据库的访问次数,提高应用程序的性能。当需要访问对象时,首先从缓存中读取序列化后的对象,如果缓存中存在则直接反序列化为原始对象,否则再从数据库中读取。

  • 减少内存占用:序列化后的对象通常比原始对象在内存中占用的空间更小,因此将序列化后的对象存储在缓存中可以更有效地利用缓存空间。

5. 远程方法调用(RMI)

  • 跨JVM调用:RMI允许Java对象在不同的Java虚拟机(JVM)之间调用方法。通过序列化,可以将方法调用的参数和返回值封装为字节流,并通过网络发送给远程JVM。远程JVM在收到字节流后,通过反序列化将其恢复为原始参数或返回值,并执行相应的方法调用。

注意事项

  • 安全性:序列化和反序列化过程中可能存在安全风险,因为恶意用户可能通过构造特殊的字节流来攻击系统.。

  • 性能影响:对于大量数据的序列化和反序列化操作,可能会对系统的性能产生影响。

3、序列化&反序列化位于TCP/IP协议哪一层?

image.png

如上图所示,OSI 七层协议模型中:

  • - 表示层做的事情主要就是对应用层的用户数据进行处理转换为二进制流。

  • 反过来的话,就是将二进制流转换成应用层的用户数据。这不就对应的是序列化和反序列化么?

因为,OSI 七层协议模型中的应用层、表示层和会话层对应的都是 TCP/IP 四层模型中的应用层,所以序列化协议属于 TCP/IP 协议应用层的一部分。

4、常见的序列化&反序列化

JDK自带

核心特点

  • 简洁性:JDK的序列化机制极其简便,仅需目标类实现Serializable接口即可,该接口作为标记接口,不强制要求实现任何方法,仅用于指示该类支持序列化。

  • 版本控制:每个可序列化的类都隐式或显式地拥有一个serialVersionUID,用以在反序列化时确保类版本的一致性。若未显式指定,编译器将自动生成此ID。

优点

  • 易用性:实现序列化仅需一行代码(实现接口),极大降低了开发门槛。

  • 数据完整性:由于JDK序列化机制会将对象的类描述、属性元数据及继承关系等信息一并序列化,确保了数据的完整性和恢复时的准确性。

缺点

  • 跨语言局限性:作为JDK的一部分,其序列化格式高度依赖Java语言,因此不支持跨语言间的无缝传输与反序列化。

  • 性能开销:由于序列化过程中包含了大量的元数据,导致生成的二进制流体积较大,增加了网络传输或文件存储的负担,降低了性能。

  • 安全风险:序列化数据中包含了类的详细信息,若未妥善处理,易受到反序列化攻击,攻击者可能构造恶意数据以实例化非预期对象,造成系统安全漏洞。

实例代码:

实体对象:Request

  • 实体对象必要要继承Serializable接口,如果某个属性不需要序列化只需要添加transient修饰符或者static,代表当前属性不属于序列化,既是反序列化也不会得到当前这个属性的值
package com.zhz.test.serialization.jdk;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serial;
import java.io.Serializable;

/**
 * @author zhouhengzhe
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Request implements Serializable {

    @Serial
    private static final long serialVersionUID = -938293548547683122L;

    private String id;

    private String url;

    private String method;

    private String requestBody;

    private String requestHeader;

    private String requestParam;

    private String requestCookie;
}

测试序列化&反序列化的代码:

  @Test
    @SneakyThrows
    public void testJdkSerialization() {
        Request request = Request
                .builder()
                .id("1")
                .url("http://www.baidu.com")
                .method("GET")
                .requestBody("requestBody")
                .requestHeader("requestHeader")
                .requestParam("requestParam")
                .requestCookie("requestCookie")
                .build();
        //序列化成二进制流
        System.out.println("序列化 init-------------------");
        FileOutputStream fileOutputStream = new FileOutputStream("D:\ideaproject\test\src\test\java\com\zhz\test\serialization\jdk\request.txt");
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        objectOutputStream.writeObject(request);
        objectOutputStream.close();
        fileOutputStream.close();
        System.out.println("序列化 final-------------------");
        //反序列化为Java对象
        System.out.println("反序列化 init-------------------");
    //当前文件路径为生成后的序列化的文件的路径
        FileInputStream fileInputStream = new FileInputStream("D:\ideaproject\test\src\test\java\com\zhz\test\serialization\jdk\request.txt");
        ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        Request readRequest = (Request) objectInputStream.readObject();
        System.out.println(readRequest);
        System.out.println("反序列化 final-------------------");
    }
  • 序列化:

    • ObjectOutputStream 接收一个实现了 OutputStream 接口的流作为参数,该流指定了序列化后对象数据的输出目标(如文件、网络套接字等)。通过灵活地选择不同类型的 OutputStream 实现(如 FileOutputStream 用于文件写入,ByteArrayOutputStream 用于内存中的字节流),可以方便地将序列化后的对象数据保存到所需的位置。
  • 反序列化

    • ObjectInputStream 与 FileInputStream 结合使用,将之前序列化到文件中的对象数据读取回来,并重新构造为Java中的对象实例。这个过程允许程序从持久化存储(如文件系统)中恢复对象状态,以便进一步使用。

JSON

为什么使用JSON?

  • 易于阅读和编写:JSON的语法非常简单,易于人类阅读和编写。它基于JavaScript编程语言的语法,但是独立于语言,任何能够解析JSON的编程语言都可以轻松地读取和生成JSON数据。

  • 易于解析和生成:由于JSON的语法简单且结构清晰,大多数现代编程语言都提供了内置的JSON解析器和生成器,使得JSON数据的处理变得非常便捷。

  • 轻量级:JSON数据格式紧凑,占用的带宽和存储空间相对较小。这对于网络传输尤为重要,因为较小的数据量意味着更快的传输速度和更低的传输成本。

  • 跨平台兼容性:JSON是一种独立于语言的格式,几乎所有的编程语言都支持JSON。这使得JSON成为在不同系统和平台之间传输数据的理想选择。

  • 易于扩展:JSON数据结构支持嵌套,可以轻松地表示复杂的数据关系。此外,随着Web应用程序的不断发展,JSON还提供了丰富的扩展机制,如JSON Schema等,以支持更复杂的数据验证和转换。

  • 广泛的应用场景:JSON广泛应用于Web开发中,特别是在AJAX(Asynchronous JavaScript and XML)和RESTful Web服务中。AJAX允许网页在不重新加载整个页面的情况下,与服务器交换数据并更新部分网页内容,而JSON则是这种数据交换的常用格式。RESTful Web服务则通过HTTP请求和响应来交换JSON格式的数据,实现资源的表示、获取、更新和删除等操作。

  • 支持多种数据类型:JSON支持多种数据类型,包括字符串、数字、布尔值、数组、对象(即键值对的集合)和null。这使得JSON能够灵活地表示各种复杂的数据结构。

使用场景

  1. Web应用开发:

    • 前后端数据交换:在Web开发中,JSON是前后端之间进行数据交换的常用格式。前端通过AJAX请求从后端获取JSON格式的数据,并在页面上展示或处理这些数据。

    • 配置文件:JSON也常被用作配置文件格式,因为它易于阅读和编写,同时支持复杂的嵌套结构。

  2. 移动应用开发:

    • 类似于Web应用开发,移动应用也经常使用JSON进行数据的接收和发送。移动应用通过HTTP请求与服务器通信,通常服务器会返回JSON格式的数据。
  3. RESTful API:

    • RESTful API(Representational State Transfer)是一种网络应用程序的设计风格和开发方式,它使用HTTP请求方法(如GET、POST、PUT、DELETE等)来操作资源。JSON是RESTful API中最常用的数据交换格式之一。
  4. 数据交换和存储:

    • 在不同系统或组件之间交换数据时,JSON是一种理想的选择。因为它不仅易于人类阅读,而且易于机器解析。

    • 一些NoSQL数据库(如MongoDB)使用JSON格式存储数据,这使得数据的存储和查询更加灵活和方便。

  5. 轻量级数据处理:

    • 对于一些需要轻量级数据处理的应用(如日志分析、数据分析等),JSON可以作为一种中间格式,用于数据的传输和处理。
  6. 跨语言通信:

    • JSON是一种独立于语言的格式,因此它非常适合用于不同编程语言之间的数据交换和通信。
  7. Web服务和微服务:

    • 在构建Web服务和微服务时,JSON常被用作服务之间的通信协议。服务之间通过HTTP请求交换JSON格式的数据,以实现业务逻辑的解耦和服务的独立部署。
  8. 实时通信:

    • 在一些实时通信的应用中(如Web聊天应用、实时通知系统等),JSON也被用作消息格式。客户端和服务器之间通过WebSocket等协议交换JSON格式的消息,以实现实时通信和数据更新。

实践

fastjson&fastjson2(版本:1.2.83_noneautotype)

《fastjson》

jackson

《jackson》

gson

《Gson》

XML

Hession

Kryo

ProtoStuff

Protobuf

msgpack

FST

thrift

avro

#你都收到了哪些公司的感谢信?##牛客在线求职答疑中心##牛客创作赏金赛##机械制造笔面经##牛客解忧铺#
消息队列底层剖析原理 文章被收录于专栏

主要是从内存管理,序列化,异步的产生等各种底层机制剖析mq ,让大家从零到一学一个mq

全部评论

相关推荐

点赞 2 评论
分享
牛客网
牛客企业服务