在 Java 世界里让对象“旅行”:序列化与反序列化

        Java 生态里关于 JSON 的序列化与反序列化(以下简称“序列化”)是一个久经考验的话题,却常因框架繁多、配置琐碎而让初学者望而却步。本文将围绕一段极简的 JsonUtils 工具类展开,以 FastJSON 与 Jackson 两大主流实现为例,从原理到实践、从特性到隐患,做一次系统梳理。文章力求以学术写作之严谨,帮助读者在 3000 字左右完成一次由点及面的进阶。


目录

一、为什么需要“工具类”而非直接调用框架 API

示例代码段

二、FastJSON 实现细节与行为解读

2.1 序列化:统一日期格式与循环引用控制

2.2 反序列化:TypeReference 的价值

2.3 异常策略:IllegalArgumentException 而非底层异常

三、Jackson 实现细节与行为解读

3.1 ObjectMapper 的线程安全

3.2 空 Bean 与日期格式

3.3 异常处理:IOException 的简化

四、横向对比:FastJSON vs Jackson

五、从工具类到项目落地:一个完整的演进故事

5.1 迁移步骤

5.2 兼容性陷阱

六、再谈防御式编程:边界条件的“三重门”

七、小结与展望


一、为什么需要“工具类”而非直接调用框架 API

        无论 FastJSON 还是 Jackson,其 API 都足够简洁:JSON.toJSONString(obj)objectMapper.writeValueAsString(obj) 即可完成序列化。然而生产环境中,我们往往需要在“一致性”“防御式编程”“可追踪”“可扩展”四个维度做额外约束。

  1. 一致性:日期格式、空值策略、循环引用检测等行为必须全局统一。
  2. 防御式编程:对 null、空串、非法 JSON 的入参给出明确兜底。
  3. 可追踪:异常信息须携带上下文(对象类型、原始 JSON 片段)。
  4. 可扩展:未来切换实现(如从 FastJSON 迁移到 Jackson)时业务代码零改动。

        因此,一个 JsonUtils 的存在绝非“重复造轮子”,而是对底层实现做“策略封装”。下文的两段代码正是这一思路的极简落地。

示例代码段

//FastJSON
public final class JsonUtils {private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class);// ========== 构造器 ==========private JsonUtils() {}// ========== 序列化 ==========public static String toJson(Object obj) {if (obj == null) {return "null";}try {return JSON.toJSONString(obj,SerializerFeature.DisableCircularReferenceDetect,SerializerFeature.WriteDateUseDateFormat); // 统一日期格式} catch (Exception e) {logger.error("Serialize object to JSON failed. Object={}", obj, e);throw new IllegalArgumentException("JSON serialize error", e);}}// ========== 反序列化(单个对象) ==========public static <T> T fromJson(String json, Class<T> clazz) {if (json == null || json.isEmpty()) {return null;}try {return JSON.parseObject(json, clazz);} catch (Exception e) {logger.error("Deserialize JSON to {} failed. JSON={}", clazz.getSimpleName(), json, e);throw new IllegalArgumentException("JSON deserialize error", e);}}// ========== 反序列化(复杂泛型,如 List<User>) ==========public static <T> T fromJson(String json, TypeReference<T> typeRef) {if (json == null || json.isEmpty()) {return null;}try {return JSON.parseObject(json, typeRef);} catch (Exception e) {logger.error("Deserialize JSON to {} failed. JSON={}", typeRef.getType(), json, e);throw new IllegalArgumentException("JSON deserialize error", e);}}
}
//Jackson
public final class JsonUtils {private static final ObjectMapper MAPPER = new ObjectMapper().disable(SerializationFeature.FAIL_ON_EMPTY_BEANS).setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));private JsonUtils() {}public static String toJson(Object obj) {if (obj == null) return "null";try {return MAPPER.writeValueAsString(obj);} catch (JsonProcessingException e) {throw new IllegalArgumentException("Serialize error", e);}}public static <T> T fromJson(String json, Class<T> clazz) {if (json == null || json.isEmpty()) return null;try {return MAPPER.readValue(json, clazz);} catch (IOException e) {throw new IllegalArgumentException("Deserialize error", e);}}
}

二、FastJSON 实现细节与行为解读

        FastJSON 由阿里巴巴开源,以“快”著称,实现上大量依赖 ASM 动态字节码生成,将反射开销降至极低。在给出的 FastJSON 版 JsonUtils 中,三条语句几乎涵盖日常 90% 的场景。

2.1 序列化:统一日期格式与循环引用控制
return JSON.toJSONString(obj,SerializerFeature.DisableCircularReferenceDetect,SerializerFeature.WriteDateUseDateFormat);
  • DisableCircularReferenceDetect 关闭循环引用检测。FastJSON 默认会为循环引用生成 $ref,这在 RESTful 返回中常因前端无法解析而踩坑。关闭后,若实际出现循环引用将直接抛 JSONException,用“快速失败”换取“数据干净”。

  • WriteDateUseDateFormat 强制使用全局日期格式(yyyy-MM-dd HH:mm:ss)。FastJSON 内部维护一个 DateFormat 线程局部变量,因此该配置对性能几乎无损耗。

2.2 反序列化:TypeReference 的价值
public static <T> T fromJson(String json, TypeReference<T> typeRef)

        Java 类型擦除导致 List<User> 在运行时只剩 List。FastJSON 的 TypeReference 借助匿名内部类保存泛型签名,绕过擦除,反序列化时即可还原完整类型。这一点在 Jackson 中对应 TypeReference 同名类,设计思路如出一辙。

2.3 异常策略:IllegalArgumentException 而非底层异常

        FastJSON 抛出的 JSONException 继承自 RuntimeException,工具类将其包装为 IllegalArgumentException,语义上更接近“参数非法”。这一转换使得调用方无需显式捕获受检异常,同时保持日志链路完整。


三、Jackson 实现细节与行为解读

        Jackson 是 Spring 生态的默认 JSON 方案,模块丰富、扩展点繁多。在 JsonUtils 的 Jackson 实现中,配置集中在静态 ObjectMapper 的初始化块。

3.1 ObjectMapper 的线程安全

        官方文档明确指出:ObjectMapper 在配置完成后是线程安全的。因此工具类将其声明为 static final,避免重复创建带来的元数据开销(SerializerProviderDeserializerCache 等)。但需注意,若在运行时调用 setXxx 方法修改配置,则线程安全假设将被打破。

3.2 空 Bean 与日期格式
MAPPER.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS).setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
  • FAIL_ON_EMPTY_BEANS 默认开启,当对象无任何可序列化属性时抛异常。关闭后,此类对象会被序列化为 {},避免 DTO 在演进过程中因新增字段全部 @JsonIgnore 而意外崩溃。

  • SimpleDateFormat 非线程安全,但 ObjectMapper 会将其包裹成线程局部变量,因此配置一次即可。

3.3 异常处理:IOException 的简化

        Jackson 的 writeValueAsString 声明抛出 JsonProcessingException(继承 IOException)。工具类同样将其转换为 IllegalArgumentException,与 FastJSON 保持行为统一,降低上层心智负担。


四、横向对比:FastJSON vs Jackson

维度FastJSONJackson
性能高(ASM 生成字节码)中高(3.x 版本已大幅优化)
默认日期格式时间戳时间戳
循环引用处理默认使用 $ref默认抛出 JsonMappingException
泛型反序列化TypeReferenceTypeReference(同名类)
安全配置(autoType)曾出现 RCE 漏洞,需开启 safemode默认白名单机制,漏洞面更小
社区活跃度国内高,国际一般国际主流,Spring 默认
扩展性支持 SerializeFilter 等扩展模块机制丰富(Joda、Kotlin 等)

注:性能差异在大多数业务场景下可忽略,应优先考虑可维护性与安全。


五、从工具类到项目落地:一个完整的演进故事

        假设某电商系统早期采用 FastJSON,后因安全审计要求全面迁移至 Jackson。若直接使用框架 API,则改动面巨大;而借助 JsonUtils,仅需替换实现即可。

5.1 迁移步骤
  1. 保留原有 JsonUtils 类签名,内部实现替换为 Jackson。
  2. 通过全局搜索验证无直接调用 JSON.parseXxx 的代码。
  3. 运行单元测试,重点观察日期格式、Long 型精度、BigDecimal 精度是否变化。
  4. 灰度发布,通过日志比对线上 JSON 输出差异。
5.2 兼容性陷阱
  • 浮点精度:FastJSON 默认关闭 WriteNullNumberAsZero,Jackson 需手动配置 SerializationFeature.WRITE_NULL_NUMBERS_AS_ZERO

  • Long 精度:前端 JavaScript 最大安全整数为 2^53-1,后端 Long 超过此范围需序列化为字符串。FastJSON 可配置 BrowserCompatible,Jackson 需自定义 ToStringSerializer


六、再谈防御式编程:边界条件的“三重门”

工具类虽小,却肩负第一道防线。以下三点常被忽视:

  1. null 与空串:FastJSON 允许 JSON.parseObject("", clazz) 返回 null,而 Jackson 会抛异常。工具类统一返回 null,避免调用方差异。

  2. 异常日志:必须记录原始 JSON 片段,但需脱敏(如手机号、身份证)。可引入 SPI 机制,让业务模块提供 SensitiveDataFilter

  3. 线程局部泄漏:若使用 ThreadLocal 缓存 SimpleDateFormat,务必在 Tomcat 热部署时调用 remove,防止类加载器泄露。


七、小结与展望

        序列化是“数据在 JVM 与网络之间最后一公里”的工程。FastJSON 与 Jackson 各有千秋,工具类则是屏蔽差异、沉淀团队规范的最佳载体。未来随着 Java 21 的 Vector API、Project Valhalla 的 value objects 落地,序列化的底层实现或将迎来新一轮变革。但万变不离其宗:统一配置、防御式编程、可观测三板斧,仍将长期适用。


        希望这篇 3000 字左右的梳理,能为你下一次技术选型或代码审查,提供一把“小而锋利”的瑞士军刀。

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

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

相关文章

High Speed SelectIO Wizard ip使用记录

本次实验的目的是通过VU9P开发板的6个TG接口&#xff0c;采用固定连接的方式&#xff0c;即X和X-维度互联&#xff0c;其框图如下所示&#xff1a;IP参数配置通过调用High Speed SelectIO Wizard来实现数据通路&#xff0c;High Speed SelectIO Wizard ip有24对数据通道&#x…

Execel文档批量替换标签实现方案

问题背景需求&#xff1a;俺现网班级作为维度&#xff0c;批量导出每个班级学员的数据&#xff0c;excel的个数在1k左右&#xff0c;每一张表的人数在90左右。导出总耗时在10小时左右。代码编写完成并导出现网数据后&#xff0c;发现导出的标题错了。解决方案1.通过修改代码&am…

SpringBoot配置多数据源多数据库

Springboot支持配置多数据源。默认情况&#xff0c;在yml文件中只会配置一个数据库。如果涉及到操作多个数据库的情况&#xff0c;在同实例中&#xff08;即同一个ip地址下的不同数据库&#xff09;&#xff0c;可以采用数据库名点数据库表的方式&#xff0c;实现跨库表的操作。…

Rocky9.4部署Zabbix7

一、配置安装源 rpm -Uvh https://repo.zabbix.com/zabbix/7.0/rocky/9/x86_64/zabbix-release-7.0-5.el9.noarch.rpm ​ yum clean all 二、安装Zabbix server&#xff0c;Web前端&#xff0c;agent yum install zabbix-server-mysql zabbix-web-mysql zabbix-nginx-conf z…

【Java】对象类型转换(ClassCastException)异常:从底层原理到架构级防御,老司机的实战经验

在开发中&#xff0c;ClassCastException&#xff08;类转换异常&#xff09;就像一颗隐藏的定时炸弹&#xff0c;常常在代码运行到类型转换逻辑时突然爆发。线上排查问题时&#xff0c;这类异常往往因为类型关系复杂而难以定位。多数开发者习惯于在转换前加个instanceof判断就…

探路者:用 AI 面试加速人才集结,为户外爱好者带来更专业的服务

作为深耕户外用品领域的知名品牌&#xff0c;探路者已构建起覆盖全国的销售服务网络&#xff0c;上千品种的产品矩阵更是为品牌在市场中站稳脚跟提供了有力支撑。对探路者来说&#xff0c;要持续为户外爱好者带来专业且贴心的体验&#xff0c;专业人才是核心支撑。然而&#xf…

LeetCode——面试题 05.01 插入

通过万岁&#xff01;&#xff01;&#xff01; 题目&#xff1a;一共会给四个数&#xff0c;分别是N、M、i、j&#xff0c;然后希望我们把N和M抓怒换为2进制以后&#xff0c;将M的二进制放在i到j之间的区域&#xff0c;如果M的二进制长度小于i-j1&#xff0c;则前面补0即可。最…

前端设计中如何在鼠标悬浮时同步修改块内样式

虽然只是一个小问题&#xff0c;但这个解决问题的过程也深化了自己对盒子模型的理解问题缘起正在写一个登录注册的小窗口&#xff0c;想要在鼠标悬浮阶段让按钮和文字都变色&#xff0c;但是发现实操的时候按钮和文字没办法同时变色鼠标悬停前鼠标悬停后问题分析仔细分析了下该…

航空发动机高速旋转件的非接触式信号传输系统

航空发动机是飞机动力系统的核心&#xff0c;各种关键部件如涡轮、压气机等&#xff0c;经常处于极端高温、高速旋转的工作环境中。航空发动机内的传感器数据&#xff0c;如何能够稳定可靠的通过无线的方式传输到检测太&#xff0c;一直是业内的一个难点和痛点。在这个领域&…

【postgresql按照逗号分割字段,并统计数量和求和】

postgresql按照逗号分割字段&#xff0c;并统计数量和求和postgresql按照逗号分割字段&#xff0c;并统计数量和求和postgresql按照逗号分割字段&#xff0c;并统计数量和求和 SELECT ucd, p ,tm, step, unitcd, tm_end from resource_calc_scene_rain_bound_value_plus whe…

「iOS」————继承链与对象的结构

iOS学习前言对象的底层结构isa的类型isa_tobjc_class & objc_object类信息的静态与动态存储&#xff08;ro、rw、rwe机制&#xff09;cachebits继承链isKindOfClass和isMemberOfClassisKindOfClass:isMemberofClass前言 对 对象底层结构的相关信息有点遗忘&#xff0c;简略…

代码随想录day46dp13

647. 回文子串 题目链接 文章讲解 回溯法 class Solution { public:int count 0;// 检查字符串是否是回文bool isPalindrome(string& s, int start, int end) {while (start < end) {if (s[start] ! s[end]) return false;start;end--;}return true;}// 回溯法&#…

学习随笔录

#61 学习随笔录 今日的思考 &#xff1a; 反思一下学习效率低下 不自律 或者 惰性思维 懒得思考 又或者 好高婺远 顶级自律从不靠任何意志力&#xff0c;而在于「平静如水的野心」_哔哩哔哩_bilibili 然后上面是心灵鸡汤合集 vlog #79&#xff5c;程序员远程办公的一天…

python-函数进阶、容器通用方法、字符串比大小(笔记)

python数据容器的通用方法#记住排序后容器类型会变成list容器列表 list[1,3,5,4,6,7] newListsorted(list,reverseTrue) print(newList) [7, 6, 5, 4, 3, 1]list[1,3,5,4,6,7] newListsorted(list,reverseFalse) print(newList) [1, 3, 4, 5, 6, 7]字典排序的是字典的key字符串…

关闭chrome自带的跨域限制,简化本地开发

在开发时为了图方便,简化本地开发,懒得去后端配置允许跨域,那就可以用此方法1. 右键桌面上的Chrome浏览器图标&#xff0c;选择“创建快捷方式”到桌面。2. 在新创建的快捷方式的图标上右键&#xff0c;选择“属性”。3. 在弹出窗口中的“目标”栏中追加&#xff1a; --allow-r…

C++___快速入门(上)

第一个C程序#include<iostream> using namespace std; int main() {cout << "hello world !" << endl;return 0; }上边的代码就是用来打印字符串 “hello world !” 的&#xff0c;可见&#xff0c;与C语言还是有很大的差别的&#xff0c;接下来我…

构建企业级Docker日志驱动:将容器日志无缝发送到腾讯云CLS

源码地址:https://github.com/k8scat/docker-log-driver-tencent-cls 在现代云原生架构中,容器化应用已经成为主流部署方式。随着容器数量的快速增长,如何高效地收集、存储和分析容器日志成为了一个关键挑战。传统的日志收集方式往往存在以下问题: 日志分散在各个容器中,难…

Kafka——消费者组重平衡能避免吗?

引言 其实在消费者组到底是什么&#xff1f;中&#xff0c;我们讲过重平衡&#xff0c;也就是Rebalance&#xff0c;现在先来回顾一下这个概念的原理和用途。它是Kafka实现消费者组&#xff08;Consumer Group&#xff09;弹性伸缩和容错能力的核心机制&#xff0c;却也常常成…

使用爬虫获取游戏的iframe地址

如何通过爬虫获取游戏的iframe地址要获取网页中嵌入的游戏的iframe地址&#xff08;即iframe元素的src属性&#xff09;&#xff0c;您可以使用网络爬虫技术。iframe是HTML元素&#xff0c;用于在当前页面中嵌入另一个文档&#xff08;如游戏页面&#xff09;&#xff0c;其地址…

NTLite Ent Version

NTLite是一款专业的系统安装镜像制作工具&#xff0c;通过这款软件可以帮助用户快速生成镜像文件打好补丁&#xff0c;很多朋友在安装电脑系统的时候一般都安装了windows系统的所有Windows组件&#xff0c;其实有很多Windows组件你可能都用到不到&#xff0c;不如在安装系统时就…