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

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


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

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

应用场景

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

1.1 序列化的基础

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

示例:Person类序列化

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

在这个示例中:

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 会认为这两个类是不同的版本,无法保证一致性,从而导致反序列化失败。

示例:增加非关键字段

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 类:

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

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

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

    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转换

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手动控制不提供版本控制,需手动处理

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

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

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

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相结合的代码:

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.mzph.cn/pingmian/54678.shtml

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

相关文章

深入理解Spring Data JPA与接口编程

目录 1. 什么是Spring Data JPA? 2. 如何使用Spring Data JPA? 3. 示例代码 4. 使用Query注解 5. 拓展知识:接口编程的好处 6. 结论 在软件开发领域,接口(Interface)是一种定义了方法签名但没有实现的…

Fyne ( go跨平台GUI )中文文档-小部件 (五)

本文档注意参考官网(developer.fyne.io/) 编写, 只保留基本用法 go代码展示为Go 1.16 及更高版本, ide为goland2021.2 这是一个系列文章: Fyne ( go跨平台GUI )中文文档-入门(一)-CSDN博客 Fyne ( go跨平台GUI )中文文档-Fyne总览(二)-CSDN博客 Fyne ( go跨平台GUI…

给子组件传递dom元素引用实例方案

在实际开发中有一个比较常见的场景是,父组件写了一个el-form组件。然后里面内容很多,所以拆成了子组件。那么子组件中就有调用父组件的form实例方法的需求。到所以这就涉及一个问题,怎么把父组件的form实例传递给子应用。 这里传递后直接调用…

任意长度并行前缀和 扫描算法 《PMPP》笔记

下面的算法针对于任意长度输入 对于大数据集,首先将输入分为几段,每一段放进共享内存并用一个线程块处理,比如一个线程块使用1024个线程的话,每个块最多能处理2048个元素。 在前面代码中,一个块最后的执行结果保存到了…

数据结构:单链表实现信息管理

一、函数声明部分 #ifndef __LINK_H__ #define __LINK_H__ #include <myhead.h> typedef struct Link1 {union{int len;//用于头结点&#xff0c;统计节点个数int data;//用于正常节点&#xff0c;存储数据};struct Link1 *next;//指针域 }Link,*Plink;/**********函数声…

【JS】函数柯里化

固定某些参数&#xff0c;返回接受剩余参数的新函数&#xff0c;如果没有剩余参数&#xff0c;就调用。 将多个参数的函数转换为单个参数的函数 作用&#xff1a;参数复用&#xff0c;延迟计算… // 传入参数不限&#xff0c;不能丢失传入的参数 function add() {// 不设置形参…

C\C++内存管理详解

本次内容大纲&#xff1a; 1.C/C内存分布 大家看看下面的代码 int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1[10] { 1, 2, 3, 4 };char char2[] "abcd";char* pChar3 "abcd";int…

【Unity】检测鼠标点击位置是否有2D对象

在这里提供两种方案&#xff0c;一种是射线检测&#xff0c;另一种是非射线检测。 初始准备步骤&#xff1a; 创建2D对象&#xff08;比如2D精灵&#xff09;给要被检测的2D对象添加2D碰撞体&#xff08;必须是2D碰撞体&#xff09;创建一个空对象&#xff0c;再创建一个检测…

echarts图表刷新

图表制作完成&#xff0c;点击刷新图标&#xff0c;可以刷新。 <div class"full"><div id"funnel" class"normal"></div><div class"refreshs"><div class"titles_pic"><img src"./…

nginx+keepalived健康检查案例详解(解决nginx出现故障却不能快速切换到备份服务器的问题)

文章目录 简介配置过程前置环境请看创建健康检查脚本结果测试 简介 在我们通过nginxkeepalived实现高可用后&#xff0c;会发现nginx出现故障的时候keepalived并不会将虚拟ip切换到备份服务器上其原理就是nginx和keepalived是两个独立的服务&#xff0c;Nginx的故障状态不会触…

微信小程序-分包加载

文章目录 微信小程序-分包加载概述基本使用打包和引用原则独立分包分包预下载 微信小程序-分包加载 概述 小程序的代码通常是由许多页面、组件以及资源等组成&#xff0c;随着小程序功能的增加&#xff0c;代码量也会逐渐增加&#xff0c;体积过大就会导致用户打开速度变慢&a…

QT——多线程操作

一、单线程和多线程的区别 单线程指的是程序在执行时只有一个流程,也就是一次只能执行一个任务。当程序中某个任务需要花费大量时间时,单线程会导致整个程序阻塞,用户体验会变差。 多线程则是指程序在执行时可以同时执行多个任务,每个任务都是一个独立的线程。多线程可以…

数字IC设计\FPGA 职位经典笔试面试整理--语法篇 Verilog System Verilog(部分)

注&#xff1a; 资料都是基于网上一些博客分享和自己学习整理而成的 Verilog 1. 数据类型 Verilog一共有19种数据类型 基础四种数据类型&#xff1a;reg型&#xff0c;wire型&#xff0c;integer型&#xff0c;parameter型 reg型   reg类型是寄存器数据类型的关键字。寄存…

Spring Boot 点餐系统:您的餐饮助手

第三章 系统分析 3.1 系统设计目标 网上点餐系统主要是为了用户方便对美食信息、美食评价、美食资讯等信息进行查询&#xff0c;也是为了更好的让管理员进行更好存储所有数据信息及快速方便的检索功能&#xff0c;对系统的各个模块是通过许多今天的发达系统做出合理的分析来确定…

Firefox火狐浏览器web开发调试开启强制刷新缓存模式

场景:vuetoken过期或者修改token后&#xff0c;刷新后进不去系统! 解决&#xff1a; 火狐浏览器缓存难清理&#xff0c;用CtrlF5 CtrlR 等在谷歌和IE浏览器的快捷键没用。 火狐清理缓存比较麻烦&#xff0c;默认快捷键 Ctrl Shift Del 键是弹窗选择性清理&#xff0c;还要…

Spring、SpringBoot 框架功能学习

一. Spring核心功能 依赖注入&#xff08;DI&#xff09;&#xff1a;Spring的核心功能是通过依赖注入来管理对象之间的依赖关系。依赖注入是一种将对象的依赖关系注入到被依赖对象中的机制&#xff0c;它可以帮助降低对象之间的耦合度&#xff0c;使得代码更容易维护和测试。 …

springboot接入emqx的mqtt

需求背景 物联网设备需要通过mqtt协议传输,这里记录一下,注意,这篇文章不能接入阿里云的mqtt,本人已经试过,会报错。 开发教程 1、EMQX安装部署 -- 1 安装必要的依赖 sudo yum install -y yum-utils device-mapper-persistent-data lvm2-- 2 设置repo库 sudo yum-confi…

原腾讯云AI产品线项目经理李珊受邀为第四届中国项目经理大会演讲嘉宾

全国项目经理专业人士年度盛会 原腾讯云AI产品线项目经理、资深项目管理专家李珊女士受邀为PMO评论主办的全国项目经理专业人士年度盛会——2024第四届中国项目经理大会演讲嘉宾&#xff0c;演讲议题为&#xff1a;AI助力项目经理的决策支持系统。大会将于10月26-27日在北京举办…

形象解释一下泛化任务和外推任务

泛化任务和外推任务都是神经网络在训练后面临的挑战&#xff0c;但它们的核心区别在于模型面临的数据分布范围。下面我来形象解释这两个任务&#xff0c;并说明它们的不同之处。 1. 泛化任务&#xff08;Generalization Task&#xff09;&#xff1a; 形象解释&#xff1a;假设…

AR传送门+特定区域显示内容+放大镜 效果着色器使用

AR传送门特定区域显示内容放大镜 效果 关键词&#xff1a;Portal Mask 1、教程链接&#xff1a; AR 传送门教程 Unity - Portal Mask Implementation - Part 4_哔哩哔哩_bilibili 应用案例效果&#xff1a; 2、案例下载地址&#xff1a;使用unity 2021.3.33f1 obi 工具…