介紹
在Java中,序列化和反序列化是一種將對象轉換為字節流或將字節流轉換為對象的機制。通過序列化,可以將對象存儲到文件中、傳輸到網絡上,或者在分布式系統中進行對象的傳遞。本文將詳細介紹Java序列化和反序列化的原理、使用方法和常見應用場景,并提供一些示例代碼。
原理
Java序列化機制是基于對象的類結構進行的。當一個對象需要被序列化時,Java會將其轉換為字節流,包括對象的數據和類的信息。這個字節流可以存儲到文件中、傳輸到網絡上,或者在分布式系統中傳遞給其他節點。
反序列化是將字節流轉換回對象的過程。在反序列化過程中,Java會使用字節流中的信息重構對象,并將其重新加載到內存中。
應用場景
1、持久化存儲
通過序列化,可以將對象存儲到文件中,實現持久化存儲。這在很多場景下都是非常有用的,例如保存應用程序的配置信息、保存用戶的數據等。
2、網絡傳輸
序列化可以將對象轉換為字節流,從而方便地在網絡上進行傳輸。這在分布式系統、RPC調用等場景中非常常見。通過序列化,可以將對象打包成字節流,發送到遠程節點,然后在遠程節點上進行反序列化,恢復為原始對象。
3、緩存機制
一些緩存系統使用序列化來存儲和檢索對象。當需要將對象存儲到緩存中或從緩存中讀取對象時,可以將對象序列化為字節流,并將其存儲在緩存系統中。
java序列化知識點
示例
jackson(springboot默認)
Jackson API指南
因為spring-boot-starter-web 默認 有jackson 故無需引入
非springboot項目
引入jackson-databind相當于引入了jackson-core和jackson-annotations。
<dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.13.5</version>
</dependency>
工具類
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.List;@Slf4j
public class JacksonUtil {private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();private static final ObjectMapper OBJECT_MAPPER_SNAKE_CASE = new ObjectMapper();// 日期格式化private static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";static {//對象的所有字段全部列入OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.ALWAYS);//取消默認轉換timestamps形式OBJECT_MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);//忽略空Bean轉json的錯誤OBJECT_MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);//所有的日期格式都統一為以下的樣式,即yyyy-MM-dd HH:mm:ssOBJECT_MAPPER.setDateFormat(new SimpleDateFormat(STANDARD_FORMAT));//忽略 在json字符串中存在,但是在java對象中不存在對應屬性的情況。防止錯誤OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);}static {//對象的所有字段全部列入OBJECT_MAPPER_SNAKE_CASE.setSerializationInclusion(JsonInclude.Include.ALWAYS);//取消默認轉換timestamps形式OBJECT_MAPPER_SNAKE_CASE.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);//忽略空Bean轉json的錯誤OBJECT_MAPPER_SNAKE_CASE.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);//所有的日期格式都統一為以下的樣式,即yyyy-MM-dd HH:mm:ssOBJECT_MAPPER_SNAKE_CASE.setDateFormat(new SimpleDateFormat(STANDARD_FORMAT));//忽略 在json字符串中存在,但是在java對象中不存在對應屬性的情況。防止錯誤OBJECT_MAPPER_SNAKE_CASE.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);//轉換為下劃線OBJECT_MAPPER_SNAKE_CASE.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);}private JacksonUtil() {}/*** 對象轉Json格式字符串** @param obj 對象* @return Json格式字符串*/public static <T> String objToString(T obj) {if (obj == null) {return null;}try {return obj instanceof String ? (String) obj : OBJECT_MAPPER.writeValueAsString(obj);} catch (JsonProcessingException e) {log.warn("Parse Object to String error : {}", e.getMessage());return null;}}/*** 對象轉file* @param fileName* @param obj*/public static void objToFile(String fileName,Object obj){if (obj == null){return;}try {OBJECT_MAPPER.writeValue(new File(fileName),obj);} catch (IOException e) {e.printStackTrace();}}/*** 對象轉Json格式字符串; 屬性名從駝峰改為下劃線形式** @param obj 對象* @return Json格式字符串*/public static <T> String objToStringFieldSnakeCase(T obj) {if (obj == null) {return null;}try {ObjectMapper objectMapper = OBJECT_MAPPER_SNAKE_CASE;return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj);} catch (JsonProcessingException e) {log.warn("Parse Object to String error : {}", e.getMessage());return null;}}/*** 字符串轉換為自定義對象; 屬性名從下劃線形式改為駝峰** @param str 要轉換的字符串* @param clazz 自定義對象的class對象* @return 自定義對象*/public static <T> T stringToObjFieldLowerCamelCase(String str, Class<T> clazz) {if (StringUtils.isEmpty(str) || clazz == null) {return null;}try {return clazz.equals(String.class) ? (T) str : OBJECT_MAPPER_SNAKE_CASE.readValue(str, clazz);} catch (Exception e) {log.warn("Parse String to Object error : {}", e.getMessage());return null;}}/*** 字符串轉換為自定義對象(List); 屬性名從下劃線形式改為駝峰** @param str 要轉換的字符串* @param typeReference 自定義對象的typeReference List 對象* @return 自定義對象*/public static <T> List<T> stringToListFieldLowerCamelCase(String str, TypeReference<List<T>> typeReference) {if (StringUtils.isEmpty(str) || typeReference == null) {return null;}try {return OBJECT_MAPPER_SNAKE_CASE.readValue(str, typeReference);} catch (Exception e) {log.warn("Parse String to Object error : {}", e.getMessage());return null;}}/*** 對象轉Json格式字符串(格式化的Json字符串)** @param obj 對象* @return 美化的Json格式字符串*/public static <T> String objToStringPretty(T obj) {if (obj == null) {return null;}try {return obj instanceof String ? (String) obj : OBJECT_MAPPER.writerWithDefaultPrettyPrinter().writeValueAsString(obj);} catch (JsonProcessingException e) {log.warn("Parse Object to String error : {}", e.getMessage());return null;}}/*** 字符串轉換為自定義對象** @param str 要轉換的字符串* @param clazz 自定義對象的class對象* @return 自定義對象*/public static <T> T stringToObj(String str, Class<T> clazz) {if (StringUtils.isEmpty(str) || clazz == null) {return null;}try {return clazz.equals(String.class) ? (T) str : OBJECT_MAPPER.readValue(str, clazz);} catch (Exception e) {log.warn("Parse String to Object error : {}", e.getMessage());return null;}}/*** 字符串轉換為自定義字段轉為list* @param str* @param typeReference* @param <T>* @return*/public static <T> T stringToObj(String str, TypeReference<T> typeReference) {if (StringUtils.isEmpty(str) || typeReference == null) {return null;}try {return (T) (typeReference.getType().equals(String.class) ? str : OBJECT_MAPPER.readValue(str, typeReference));} catch (IOException e) {log.warn("Parse String to Object error", e);return null;}}public static <T> T stringToObj(String str, Class<?> collectionClazz, Class<?>... elementClazzes) {JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructParametricType(collectionClazz, elementClazzes);try {return OBJECT_MAPPER.readValue(str, javaType);} catch (IOException e) {log.warn("Parse String to Object error : {}" + e.getMessage());return null;}}
}
Java 對象與 Json 字符串的轉換
方法 | 說明 |
---|---|
String writeValueAsString(Object value) | 1、用于將任何 Java 對象(如 POJO、List、Set、Map等)序列化為 json 字符串,如果對象中某個屬性的值為 null,則默認也會序列化為 null; 2、如果 value 為 null,返回序列化的結果也返回 null |
byte[] writeValueAsBytes(Object value) | 將 java 對象序列化為字節數組 |
writeValue(File resultFile, Object value) | 將 java 對象序列化并輸出指定文件中 |
writeValue(OutputStream out, Object value) | 將 java 對象序列化并輸出到指定字節輸出流中 |
writeValue(Writer w, Object value) | 將 java 對象序列化并輸出到指定字符輸出流中 |
T readValue(String content, Class valueType) | 1、從給定的 JSON 字符串反序列化為 Java 對象; 2、content 為空或者為 null,都會報錯; 3、valueType 表示反序列化的結果對象,可以是任何 java 對象,比如 POJO、List、Set、Map 等等. |
T readValue(byte[] src, Class valueType) | 將 json 內容的字節數組反序列化為 java 對象 |
T readValue(File src, Class valueType) | 將本地 json 內容的文件反序列化為 java 對象 |
T readValue(InputStream src, Class valueType) | 將 json 內容的字節輸入流反序列化為 java 對象 |
T readValue(Reader src, Class valueType) | 將 json 內容的字符輸入流反序列化為 java 對象 |
T readValue(URL src, Class valueType) | 通過網絡 url 地址將 json 內容反序列化為 java 對象 |
Json 字符串內容反序列化為 Json 節點對象
方法 | 說明 |
---|---|
JsonNode readTree(String content) | 將 JSON 字符串反序列化為 JsonNode 對象,即 json 節點對象 |
JsonNode readTree(URL source) | 對網絡上的 json 文件進行反序列化為 json 節點對象 |
JsonNode readTree(InputStream in) | 對 json 文件輸入流進行反序列化為 json 節點對象 |
JsonNode readTree(byte[] content) | 對 json 字節數組反序列化為 json 節點對象 |
JsonNode readTree(File file) | 將本地 json 文件反序列為為 json 節點對象 |
Java 對象與 Json 節點對象的轉換
方法 | 說明 |
---|---|
T convertValue(Object fromValue, Class toValueType) | 將 Java 對象(如 POJO、List、Map、Set 等)序列化為 Json 節點對象。 |
T treeToValue(TreeNode n, Class valueType) | json 樹節點對象轉 Java 對象(如 POJO、List、Set、Map 等等) TreeNode 樹節點是整個 json 節點對象模型的根接口。 |
JsonNode 樹模型 Json 節點
1、JsonNode 表示 json 節點,整個節點模型的根接口為 TreeNode,json 節點主要用于手動構建 json 對象。
2、JsonNode 有各種數據類型的實現類,其中最常用的就是 ObjectNode 與 ArrayNode,前者表示 json 對象,后者表示 json 對象數組。
3、json 節點對象可以通過 JsonNodeFactory 創建,如 JsonNodeFactory.instance.objectNode();
JsonNode json節點通用方法
方法 | 說明 |
---|---|
JsonNode get(String fieldName) | 用于訪問對象節點的指定字段的值,如果此節點不是對象、或沒有指定字段名的值,或沒有這樣名稱的字段,則返回 null。 |
JsonNode get(int index) | 訪問數組節點的指定索引位置上的值,對于其他節點,總是返回 null。 如果索引小于0,或等于、大于節點大小,則返回 null,對于任何索引都不會引發異常。 |
boolean isArray() | 判斷此節點是否為 {@link ArrayNode} 數組節點 |
boolean isObject() | 如果此節點是對象節點,則返回 true,否則返回 false |
int size() | 獲取 json 節點的大小,比如 json 對象中有多少鍵值對,或者 json 數組中有多少元素。 |
ObjectNode deepCopy() | json 節點對象深度復制,相當于克隆 |
Iterator fieldNames() | 獲取 json 對象中的所有 key |
Iterator elements() | 如果該節點是JSON數組或對象節點,則訪問此節點的所有值節點,對于對象節點,不包括字段名(鍵),只包括值,對于其他類型的節點,返回空迭代器。 |
boolean has(int index) | 檢查此節點是否為數組節點,并是否含有指定的索引。 |
boolean has(String fieldName) | 檢查此節點是否為 JSON 對象節點并包含指定屬性的值。 |
Json 屬性的值轉為 java 數據類型
方法 | 說明 |
---|---|
int asInt() int asInt(int defaultValue) | asInt():嘗試將此節點的值轉換為 int 類型,布爾值 false 轉換為 0,true 轉換為 1。如果不能轉換為 int(比如值是對象或數組等結構化類型),則返回默認值 0 ,不會引發異常。 asInt(int defaultValue):設置默認值 |
boolean asBoolean() boolean asBoolean(boolean defaultValue) | asBoolean():嘗試將此節點的值轉換為 Java 布爾值,0以外的整數映射為true,0映射為false,字符串“true”和“false”映射到相應的值。如果無法轉換為布爾值(包括對象和數組等結構化類型),則返回默認值 false,不會引發異常。 asBoolean(boolean defaultValue):可以自己設置默認值。 |
String asText() String asText(String defaultValue) | 如果節點是值節點(isValueNode 返回 true),則返回容器值的有效字符串表示形式,否則返回空字符串。 |
long asLong() long asLong(long defaultValue) | 與 asInt 同理 |
double asDouble() double asDouble(double defaultValue) | 嘗試將此節點的值轉換為 double,布爾值轉換為0.0(false)和1.0(true),字符串使用默認的Java 語言浮點數解析規則進行解析。 如果表示不能轉換為 double(包括對象和數組等結構化類型),則返回默認值 0.0,不會引發異常。 |
BigInteger bigIntegerValue() | 返回此節點的整數值(BigDecimal),當且僅當此節點為數字時(isNumber}返回true)。 對于其他類型,返回 BigInteger.ZERO。 |
boolean booleanValue() | 用于訪問 JSON 布爾值(值文本“true”和“false”)的方法,對于其他類型,始終返回false |
BigDecimal decimalValue() | 返回此節點的浮點值 BigDecimal, 當且僅當此節點為數字時(isNumber 返回true),對于其他類型,返回 BigDecimal.ZERO |
double doubleValue() | 返回此節點的64位浮點(雙精度)值,當且僅當此節點為數字時(isNumber返回true),對于其他類型,返回0.0。 |
float floatValue() | 返回此節點的32位浮點值,當且僅當此節點為數字時(isNumber返回true),對于其他類型,返回0.0。 |
int intValue() long longValue() Number numberValue() short shortValue() String textValue() | 返回此節點的 int、long、Number、short、String 值。 |
ObjectNode 對象節點常用方法
方法 | 說明 |
---|---|
ObjectNode put(String fieldName, String v) ObjectNode put(String fieldName, int v) | 1、將字段的值設置為指定的值,如果字段已經存在,則更新值,value 可以為 null. 2、其它 8 種基本數據類型以及 String、BigDecimal、BigInteger 都可以 put,但是沒有 Date 類型 3、Date 日期類型只能通過 Long 長整型設置 |
ArrayNode putArray(String fieldName) | 構造新的 ArrayNode 子節點,并將其作為此 ObjectNode 的字段添加。 |
ObjectNode putNull(String fieldName) | 為指定字段添加 null 值 |
ObjectNode putObject(String fieldName) | 構造新的 ObjectNode 字節的,并將其作為此 ObjectNode 的字段添加。 |
替換與刪除元素
方法 | 說明 |
---|---|
JsonNode replace(String fieldName, JsonNode value) | 將特定屬性的值替換為傳遞的值,字段存在時更新,不存在時新增 |
JsonNode set(String fieldName, JsonNode value) | 設置指定屬性的值為 json 節點對象,字段存在時更新,不存在時新增,類似 replace 方法 |
JsonNode setAll(Map<String,? extends JsonNode> properties) | 同時設置多個 json 節點 |
JsonNode setAll(ObjectNode other) | 添加給定對象(other)的所有屬性,重寫這些屬性的任何現有值 |
ArrayNode withArray(String propertyName) | 將 json 節點轉為 json 數組對象 |
ObjectNode with(String propertyName) | 將 json 節點轉為 ObjectNode 對象 |
JsonNode remove(String fieldName) | 刪除指定的 key,返回被刪除的節點 |
ObjectNode remove(Collection fieldNames) | 同時刪除多個字段 |
ObjectNode removeAll() | 刪除所有字段屬性 |
JsonNode without(String fieldName) | 刪除指定的 key,底層也是 remove |
ObjectNode without(Collection fieldNames) | 同時刪除多個字段,底層也是 removeAll |
ArrayNode 數組節點常用方法
方法 | 說明 |
---|---|
ArrayNode add(String v) | 將指定的字符串值添加到此 json 數組的末尾,其它數據類型也是同理。 除了可以添加 String 類型,還有 Java 的 8 種基本數據類型,以及 BigDecimal、BigInteger、JsonNode 類型。 |
ArrayNode addAll(ArrayNode other) | 用于添加給定數組的所有子節點 |
ArrayNode addNull() | 該方法將在此數組節點的末尾添加 null 值。 |
ArrayNode addArray() | 構造新的 ArrayNode 子節點,并將其添加到此數組節點的末尾 |
ObjectNode addObject() | 構造一個新的 ObjectNode 字節的,并將其添加到此數組節點的末尾 |
Jsonson 注解設置 POJO 屬性
1、ObjectMapper 序列化 POJO 對象為 json 字符串時,Date 日期類型默認會轉為 long 長整型,json 字符串反序列化為 POJO 時自動將長整型的日期轉為 Date 類型。
2、Map 對象序列化為 json 字符串時,Map 中的日期值也會默認轉為 long 長整型, ObjectMapper.readValue(String content, Class valueType):反序列化為 Map 對象時,長整型的日期仍舊是長整型,不會轉回 Date。ObjectMapper.readTree(String content) 反序列化為 json 節點時,原來 Map 中的長整型的日期也會是長整型。
3、JsonNode 節點對象 put 數據時,有 8 種基本數據類型以及 String、BigDecimal、BigInteger,但是沒有 Date 類型,所以日期類型只能通過 Long 長整型設置,但是轉 POJO 對象時仍然會自動轉為 Date 類型。
4、Google 的 gson 默認不會序列化對象中值為 null 的字段,而 jackson 則不管值是否為 null,都會序列化。
5、@JsonFormat 注解加到指定對象的屬性上可以指定日期序列化的格式。
6、JsonInclude(JsonInclude.Include.NON_NULL) 添加到 POJO 對象上,表示對值為 null 的屬性不進行序列化。
jackson API
參考 jackson詳解
jackson工具類
fastjson2(安全漏洞頻發)
1. maven 引入
<dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.43</version>
</dependency>
2.直接使用即可
Account account = new .... // java類對象
// 將java對象轉化為 JSON字符串 人類可讀性好
String a= JSON.toJSONString(account);// 將 JSON字符串 轉化為 java對象
Account b = JSON.parseObject(a,Account.class);// 將java對象轉化為 byte數組 人類可讀性差
byte[] c = JSON.toJSONBytes(account);// 將 byte數組 轉化為 java對象
Account d = JSON.parseObject(c,Account.class);// 使用 JSONB 將 java對象轉化為 byte數組
byte[] e = JSONB.toBytes(account);// 使用 JSONB 將 byte數組 轉化為 java對象
Account f= JSONB.parseObject(e,Account.class);這里使用java對象作為例子 也可以使用其他的例如 基本數據類型 String類型等
gitee中文文檔
說明一下 JSONB 和 JSON的區別
- 數據格式:
JSON 是一種輕量級的數據交換格式,易于人閱讀和編寫,同時也易于機器解析和生成。它基于文本,通常以 .json 作為文件擴展名。
JSONB 是 Fastjson2 引入的二進制格式,它是為了提供高性能和高壓縮比的二進制序列化能力而設計的。JSONB 格式的數據不是為了人類的直接閱讀,而是為了在系統之間高效地傳輸和存儲。- 性能:
JSON 格式由于是基于文本的,解析和生成可能相對較慢,尤其是在處理大量數據時。
JSONB 格式由于是二進制的,解析和生成的速度通常更快,占用的存儲空間也更小。這使得 JSONB 在性能要求較高的場景中更為適用。- 兼容性:
JSON 格式具有廣泛的兼容性,幾乎所有的編程語言都有支持解析和生成 JSON 數據的庫。
JSONB 格式是 Fastjson2 特有的,雖然它可以轉換為 JSON 格式以便進行診斷和分析,但它不是所有環境中的標準格式。- 應用場景:
JSON 格式適用于需要人類參與的場景,如 API 響應、配置文件等,以及那些不需要過分關注性能的場景。
JSONB 格式適用于性能敏感的應用,如高性能 RPC 調用、大量數據的傳輸和存儲等。- 支持和生態:
JSON 格式由于其簡單和通用,有著豐富的工具和庫支持,以及廣泛的社區和生態系統。
JSONB 格式作為 Fastjson2 的一部分,雖然有阿里巴巴的支持,但相對來說,它的生態系統和社區可能不如 JSON 格式成熟.
fury(跨語言序列化組件)
待續…