Java基础-序列化
参考文献
什么是序列化
- 序列化:
Java
中的序列化机制能够将一个实例化对象信息写入到一个字节流中(只序列号对象的属性值,而不会去序列化方法),序列化后的对象可用于网络传输,或者持久化到数据库,磁盘中. - 反序列化: 需要对象的时候,在通过字节流中的信息来重构一个相同的对象.
Java中具体实现
-
Java中要使一个类可以序列化,实现
java.io.Serializable
接口是最简单的.1
2
3
4public class User implements Serializable {
private static final long serialVersionUID = 1L;
}1
2
3
4
5
6
7
8
9
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String age;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class SerializeTest {
public static void main(String[] args) throws Exception {
User user = new User();
user.setName("HoleLin");
user.setAge("18");
serialize(user);
log.info("Java序列化前的结果:{} ", user);
User deserializeUser = deserialize();
log.info("Java反序列化的结果:{} ", deserializeUser);
}
/**
* 序列化
*/
private static void serialize(User user) throws Exception {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("D:\\UserSerialize.txt")));
oos.writeObject(user);
oos.close();
}
/**
* 反序列化
*/
private static User deserialize() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("D:\\UserSerialize.txt")));
return (User) ois.readObject();
}
}
为什么要实现Serializable
接口
- 打开
writeObject
方法的源码看一下,发现方法中有这么一个逻辑,当要写入的对象是String
、Array
、Enum
、Serializable
类型的对象则可以正常序列化,否则会抛出NotSerializableException
异常。
1 | /** |
为什么要显示指定serialVersionUID
- 因为序列化对象时,如果不显示的设置
serialVersionUID
,Java在序列化会根据对象属性自动的生成一个serialVersionUID
,在进行存储或用作网络传输. - 在反序列化时,会根据对象属性自动再生成一个新的
serialVersionUID
,和序列化是生成的serialVersionUID
进行对比,两个serialVersionUID
相同则反序列化成功,否则就会抛出异常. - 故当显示的设置
serialVersionUID
后,Java序列化和反序列化对象时,生成的serialVersionUID
都是我们设定的serialVersionUID
,这样就保证了反序列化的成功.
实际场景中遇到的问题
当POJO
中的成员变量名不符合Java成员变量命名规范(驼峰式),作为接口参数无法读取的问题
环境:
SpringBoot
,JDK1.8
-
POJO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class IllegalBean {
/**
* 不符合驼峰命名规范: 全大写
*/
private String NAME;
/**
* 不符合驼峰命名规范: 首字母大写
*/
private String DeviceId;
private List<POJO> data;
public static class POJO{
private String ADDRESS;
private String AGE;
}
} -
Controller
1
2
3
4
5
6
7
8
9
public class IllegalController {
public IllegalBean illegal( { IllegalBean bean)
return bean;
}
} -
测试: 发现传值无法被映射到对应的字段上去
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// 请求体
{
"NAME": "NAME",
"DeviceId": "DeviceId",
"data": [
{
"ADDRESS": "address",
"AGE": "age"
}
]
}
// 返回值
{
"data": [
{
"age": null,
"address": null
}
],
"deviceId": null,
"name": null
} -
问题所在: 使用
@Data
生成Getter/Setter
是get/set属性名称
与SpringBoot
默认Jackson
序列化转换获取属性不一致1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// ...省略
public String getNAME() {
return this.NAME;
}
public String getDeviceId() {
return this.DeviceId;
}
public List<IllegalBean.POJO> getData() {
return this.data;
}
public void setNAME(final String NAME) {
this.NAME = NAME;
}
public void setDeviceId(final String DeviceId) {
this.DeviceId = DeviceId;
}
// ...省略 -
源码分析
- 默认采用
MappingJackson2HttpMessageConverter
转换
-
由
NAME
改为name
则可以映射到对应的位置 -
Jackson
获取的属性名称是驼峰式的,故而当传NAME
这种是无法映射到对应的位置上
- 默认采用
-
解决办法
-
方法一: 使用
@JsonProperty
注解1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class IllegalBean {
/**
* 不符合驼峰命名规范: 全大写
*/
private String NAME;
/**
* 不符合驼峰命名规范: 首字母大写
*/
private String DeviceId;
private List<POJO> data;
public static class POJO{
private String ADDRESS;
private String AGE;
}
} -
方法二: 添加阿里巴巴的
FastJson
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20@Data
public class IllegalBean {
/**
* 不符合驼峰命名规范: 全大写
*/
private String NAME;
/**
* 不符合驼峰命名规范: 首字母大写
*/
private String DeviceId;
private List<POJO> data;
@Data
public static class POJO {
private String ADDRESS;
private String AGE;
}
}- 但是会带来返回值会变成小写的情况,对于这种情况可以加
@JsonField
注解来解决
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26@Data
public class IllegalBean {
/**
* 不符合驼峰命名规范: 全大写
*/
@JSONField(name = "NAME")
private String NAME;
/**
* 不符合驼峰命名规范: 首字母大写
*/
@JSONField(name = "DeviceId")
private String DeviceId;
private List<POJO> data;
@Data
public static class POJO {
@JSONField(name = "ADDRESS")
private String ADDRESS;
@JSONField(name = "AGE")
private String AGE;
}
} - 但是会带来返回值会变成小写的情况,对于这种情况可以加
-
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 HoleLin's Blog!