Java序列化相关知识学习

每次写DTO类虽然都实现了Serializable接口,但是其并没有显示声明serialVersionUID,网上发现这样的话每次打包有改动JDK就会为其重新生成serialVersionUID.这就带来了不同版本之间的实体类可能反序列化不成功,线上RPC调用出现了问题.那么就深入探讨一下原因.

Serializable的作用
看该类的JDK注释可以发现The serialization interface has no methods or fields and serves only to identify the semantics of being serializable.也就是说Serializable是一个标识接口,和Cloneable接口等一样的效果.
如下面的User类,实现了序列化接口,并使用serialVersionUID标识其序列化对应的ID序号.
static class User implements Serializable {
private static final long serialVersionUID = 5768430629641297769L;
private String nickname;
private String passwd;
//省略get和set

如何序列化
java.io.ObjectOutputStream代表对象输出流,其使用writeObject()方法把对象实例转换为字节流然后写入到文件,或者用于网络传输.
@Test
public void testWriteObj() throws IOException {
User userDO = new User();
userDO.setNickname("测试");
userDO.setPasswd("123456");
File file = new File("user.out");
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(file));
outputStream.writeObject(userDO);//序列化写入到文件中.
outputStream.close();
}

如何反序列化
java.io.ObjectInputStream代表对象输入流,其使用readObject()方法读取序列化的字节,然后再转换为对象.
@Test
public void testReadObj() throws IOException, ClassNotFoundException {
File file = new File(base+File.separator+"user.out");
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(file));
User user = (User) inputStream.readObject();
Assert.assertTrue(StringUtils.equals(user.getNickname(),"屈定"));
Assert.assertTrue(StringUtils.equals(user.getPasswd(),"123456"));
}

serialVersionUID的作用
按照上面代码,序列化和反序列化都是成功的,如果在已经序列化后,对User要作修改,增加一个email字段,再试试反序列化.
static class User implements Serializable {
private static final long serialVersionUID = 5768430629641297769L;
private String nickname;
private String passwd;
private String email;
}
程序会正常运行,而且这个email会被很智能的初始化为null.
修改serialVersionUID为1L再试试.
java.io.InvalidClassException: cn.edu.aust.test.ObjectTest$User; local class incompatible: stream classdesc serialVersionUID = 5768430629641297769, local class serialVersionUID = 1

报错很明显,两边类的serialVersionUID不一样,也就是说对于编译好的class,其serialVersionUID是其序列化的唯一标识,如果未显示声明JDK则会自动为其加上,可以使用命令seriserialver可以查看一个class文件的serialVersionUID,当线上版本忘记加该字段的时候该命令还是很有用处的.
seriserialver cn.edu.aust.test.ObjectTest\$User
cn.edu.aust.test.ObjectTest$User: private static final long serialVersionUID = 1L;

transient的作用
transient翻译为瞬时,也就是被其修饰的变量序列化时会忽略该字段.什么时候需要用到这个字段呢?
在Java中对象之间的关系会组成一个对象图,序列化的过程是对该对象图的遍历,那么反序列化也仍然是对该对象图的遍历.对于对象里面的对象就是递归过程,对于链表之类的数据结构递归的话很容易引起栈溢出,那么就可以使用transient忽略该字段.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值