介绍
在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(跨语言序列化组件)
待续…