在 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
英文地址,請注明出處:http://en.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;不如在安裝系統時就…