序列化与反序列化深入分析:UUID案例的实践与JSON转换对比

devtools/2024/10/18 8:22:00/

在Java开发中,序列化序列化是非常重要的概念。序列化是将对象的状态转换为字节流的过程,而反序列化则是将字节流恢复为对象的过程。本文将以UUID序列化案例和JSON转换为例,深入探讨这两者的具体实现及应用场景。


1. Java 序列化与反序列化机制

  • 序列化(Serialization):将Java对象的状态转换为字节流,便于存储或网络传输。
  • 序列化(Deserialization):从字节流恢复成对象,重建对象的状态。

应用场景

  • 对象持久化:将对象保存到磁盘文件中。
  • 网络传输:通过网络传递对象(如RMI、socket通信)。
  • 深拷贝:通过序列化和反序列化实现对象的深度克隆。

1.1 序列化的基础

任何想被序列化的Java类都需要实现java.io.Serializable接口。这是一个标记接口,无需实现任何方法,仅仅标记对象可序列化

示例:Person序列化

java">import java.io.Serializable;public class Person implements Serializable {private static final long serialVersionUID = -3273681225617595773L; // 用于版本控制private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}// getters and setters
}

serialVersionUID 解释:

serialVersionUID序列化时用来验证版本一致性的标识。每次序列化和反序列化时,JVM会比较类的serialVersionUID,如果不一致会抛出InvalidClassException

在这个示例中:

java">private static final long serialVersionUID = -3273681225617595773L;

它手动定义了一个唯一的serialVersionUID。这个值可以通过工具(如serialver命令)自动生成,也可以手动定义。手动定义的好处是可以更好地控制版本兼容性,即使类发生了轻微变化(如增加非关键字段),仍然可以正常反序列化

1.2序列化与反序列化代码示例:

当Java类使用**序列化(Serialization)**时,类的结构可能会在不同版本中发生变化,例如增加非关键字段。这时,serialVersionUID 的作用尤为重要。如果不使用serialVersionUID,任何类的变化都会导致反序列化失败,抛出InvalidClassException。而定义了serialVersionUID后,在类发生轻微变化时,仍然能够保持版本兼容性,从而避免反序列化错误。

1.2.1增加非关键字段时的情况

假设我们在原始的 Person 类中增加一个非关键字段,例如 private String address;。如果我们不定义 serialVersionUID,Java 会自动生成一个值,它依赖于类的结构信息。当类的结构(如字段、方法)发生变化时,生成的serialVersionUID也会随之改变。于是,在反序列化时,如果当前类的 serialVersionUID 和原始序列化版本的 serialVersionUID 不一致,JVM 会认为这两个类是不同的版本,无法保证一致性,从而导致反序列化失败。

示例:增加非关键字段

java">import java.io.Serializable;public class Person implements Serializable {private static final long serialVersionUID = -3273681225617595773L; // 手动定义private String name;private int age;// 新增的字段private String address;public Person(String name, int age, String address) {this.name = name;this.age = age;this.address = address;}// getters 和 setters
}

如果我们使用手动定义的 serialVersionUID序列化和反序列化时,即使增加了这个非关键字段(如 address),只要原来的字段和结构没有发生实质性破坏性变化,反序列化依然可以正常工作。这是因为手动定义的 serialVersionUID 保证了版本兼容性。

1.2.2未定义serialVersionUID的风险

如果不手动定义 serialVersionUID,Java 编译器会基于类的结构自动生成一个serialVersionUID,而这个值随着类的结构变化而改变。这样即使你仅仅添加了一个非关键字段(如 address),原来序列化的对象在反序列化时也可能会失败,导致抛出 InvalidClassException

风险场景:

  • 假设你有一个旧版本的 Person 类:

    java">public class Person implements Serializable {private String name;private int age;
    }
    

    你将这个类的对象序列化后保存在磁盘或传输到网络中。

  • 然后,你对类进行了修改,增加了一个新字段:

    java">public class Person implements Serializable {private String name;private int age;private String address; // 新增字段
    }
    
  • 如果没有手动定义 serialVersionUID序列化系统会认为这两个类的版本不同,因为serialVersionUID值已经改变,导致旧对象在反序列化时失败,抛出异常。


2. JSON 序列化与反序列化

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于阅读和写入,广泛用于网络通信。

JSON序列化与反序列化工具:Jackson

Jackson是Java中常用的JSON处理库,提供了简单的JSON序列化与反序列化能力。

示例:使用Jackson进行JSON转换

java">import com.fasterxml.jackson.databind.ObjectMapper;public class JSONExample {public static void main(String[] args) {ObjectMapper mapper = new ObjectMapper();Person person = new Person("John", 30);try {// 序列化:将对象转为JSONString jsonString = mapper.writeValueAsString(person);System.out.println("JSON String: " + jsonString);// 反序列化:将JSON转为对象Person deserializedPerson = mapper.readValue(jsonString, Person.class);System.out.println("Name: " + deserializedPerson.getName());System.out.println("Age: " + deserializedPerson.getAge());} catch (Exception e) {e.printStackTrace();}}
}

JSON序列化/反序列化与Java序列化的对比

特性Java 序列化JSON 序列化
适用场景对象持久化、网络传输、深拷贝数据传输、RESTful API
数据格式二进制格式人类可读的JSON字符串格式
跨语言支持仅限于Java环境跨语言支持,如JavaScript、Python
性能更快,直接处理字节流较慢,需解析JSON字符串
序列化后文件大小较小的字节流文件较大的JSON字符串
安全性需谨慎处理,可能有反序列化漏洞无特定安全风险,视实现情况而定
版本控制通过serialVersionUID手动控制不提供版本控制,需手动处理

UUIDJSON_146">3. 综合案例:UUID案例与JSON转换的结合

我们可以结合两者的特点,考虑实际开发中的应用场景。例如,我们有一个User类,其中包括一个UUID来唯一标识用户。我们希望既能将这个用户对象持久化到磁盘,也能将其转换为JSON传递给客户端。

示例:User类序列化与JSON转换

java">import java.io.Serializable;
import java.util.UUID;public class User implements Serializable {private static final long serialVersionUID = 1L;private UUID userId;private String username;public User(String username) {this.userId = UUID.randomUUID();this.username = username;}public UUID getUserId() {return userId;}public String getUsername() {return username;}
}

序列化与JSON相结合的代码:

java">import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.*;
import java.util.UUID;public class UserExample {public static void main(String[] args) {User user = new User("Alice");// Java序列化try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.ser"))) {out.writeObject(user);} catch (IOException e) {e.printStackTrace();}// 反序列化try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.ser"))) {User deserializedUser = (User) in.readObject();System.out.println("Deserialized User ID: " + deserializedUser.getUserId());System.out.println("Deserialized Username: " + deserializedUser.getUsername());} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}// JSON转换ObjectMapper mapper = new ObjectMapper();try {// 序列化为JSONString jsonString = mapper.writeValueAsString(user);System.out.println("JSON String: " + jsonString);// 反序列化为对象User jsonUser = mapper.readValue(jsonString, User.class);System.out.println("JSON User ID: " + jsonUser.getUserId());System.out.println("JSON Username: " + jsonUser.getUsername());} catch (IOException e) {e.printStackTrace();}}
}

4. 总结

通过对Java序列化与反序列化、JSON转换的深入对比,我们可以看到:

  1. Java序列化适用于对象持久化、网络传输等场景,但它依赖于Java环境,跨语言支持较差。
  2. JSON转换主要用于数据传输,特别是跨语言的场景,例如前后端数据交互,但性能相对Java序列化较慢。
  3. 在实际项目中,开发者可以根据具体需求选择合适的序列化方式,Java序列化适合内部使用,而JSON更适合公开API

5、重要建议!

  • 当使用Java序列化时,务必明确serialVersionUID,以确保类的版本兼容性。同时,这为未来可能的小变动提供了更多的灵活性。
  • 对于跨语言的应用,JSON转换更为普遍和实用,特别是在RESTful API或微服务架构中。


http://www.ppmy.cn/devtools/117458.html

相关文章

深度学习:神经网络--手写数字识别

目录 一、datasets 1.datasets简介 2.主要特点 二、MNIST 三、使用神经网络实现手写数字识别 1.创建数据加载器 2.判断是否使用GPU 3.创建神经网络 4.创建训练集模型 5.创建测试集模型 6.创建损失函数和优化器并训练 一、datasets 1.datasets简介 datasets是一个广…

tensorflow-dataset 内网下载 指定目录

内网下载报错 解决办法是设置环境变量,指向你的代理服务器TFDS_HTTP_PROXYhttp://xxx、TFDS_HTTPS_PROXYhttp://xxx。 留意到,赋值的是你的代理服务器,且最好协议都使用http(即使TFDS_HTTPS_PROXY也要使用http协议连服务器)。如果不这么做&a…

Elasticsearch快速入门

文章目录 Elasticsearch快速入门核心概念倒排索引基本使用索引操作创建索引类型映射[了解]数据类型[了解] 查看索引删除索引 文档操作添加文档修改文档删除文档查询文档准备数据主键查询精确查询匹配查询 Elasticsearch快速入门 核心概念 Elasticsearch是面向文档的&#xff…

JavaEE:探索网络世界的魅力——玩转UDP编程

文章目录 UDPUDP的特点UDP协议端格式校验和前置知识校验和具体是如何工作的? UDP UDP的特点 UDP传输的过程类似于寄信. 无连接: 知道对端的IP和端口号就直接进行传输,不需要建立连接.不可靠: 没有确认机制,没有重传机制,如果因为网络故障导致该段无法到达对方,UDP协议也不会…

CentOS下安装Kibana(保姆级教程)

前言 Kibana是一个开源的数据分析和可视化平台,通常与Elasticsearch一起使用,用于展示和分析大规模数据集。以下是关于Kibana的一些主要特点和功能: 数据可视化: Kibana允许用户将数据转化为交互式、实时的图形和可视化展示&…

STM32单通道ADC连续采集

0.91寸OLED屏幕大小的音频频谱,炫酷! RTT——一种代替串口打印的调试神器 经典振荡器电路及原理分析(超值文章) 超级简单的画PCB封装技巧,从此告别繁琐计算 前面介绍了关于ADC单通道单次采样软件触发的方式《STM32…

图像面积计算一般方法及MATLAB实现

一、引言 在数字图像处理中,经常需要获取感兴趣区域的面积属性,下面给出图像处理的一般步骤。 1.读入的彩色图像 2.将彩色图像转化为灰度图像 3.灰度图像转化为二值图像 4.区域标记 5.对每个区域的面积进行计算和显示 二、程序代码 %面积计算 cle…

获取 Jupyter Notebook IPython kernel 在电脑中的目录位置

获取 Jupyter Notebook IPython kernel 在电脑中的目录位置 正文 正文 在 VS code 的 terminal 中或者 Windows 的命令行中使用如下代码即可。 ipython locate运行后得到如下结果: 如图所示,我们获取到了 ipython 的位置。 如果大家觉得有用&#xf…