Java里Serializable的那些事

时间:2024-01-01 17:49:09

本文为原创文章,欢迎转载,但请注明出处http://www.cnblogs.com/yexiubiao/p/5014015.html,未在文章页面明显位置给出原文连接的,将保留追究法律责任的权利。

通过java的ObjectOutputStream、ObjectInputStream类能对实现了Serializable接口的对象实现序列化与反序列化,如下

import java.io.Serializable;

import com.alibaba.fastjson.JSON;

public class Person implements Serializable{
private static final long serialVersionUID = 1L; private int id;
private String name; public Person() {
super();
} public Person(int id, String name) {
super();
this.id = id;
this.name = name;
} public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
} @Override
public String toString() {
          // 这里偷懒用了Fastjson
return JSON.toJSONString(this);
}
}

  

序列化(写入对象):

Java里Serializable的那些事

反序列化(读取对象)

Java里Serializable的那些事

通过这样就能把一个对象存入磁盘里,并且能读取到内存里

Java里Serializable的那些事

以上是Serializable的基本用法,接下来介绍一些使用中的小细节(关于以下出现的术语,保存对象与序列化,读取对象与反序列化都分别是同一个意思)。

1,将对象序列化到本地后,对类进行修改,增加或减少字段(成员变量)或交换字段位置,然后反序列化之前保存的对象,都不会报错。

例如去掉id字段并增加newValue字段:

Java里Serializable的那些事

此时能正常读取之前序列化的对象(newValue字段因为值是null,这里默认不展示):

Java里Serializable的那些事

2,改变serialVersionUID值后,读取时发生异常

 java.io.InvalidClassException: com.ye.test.model.Movie; Incompatible class (SUID): com.ye.test.model.Movie: static final long serialVersionUID =1L; but expected com.ye.test.model.Movie: static final long serialVersionUID =2L;
因为serialVersionUID值改变后,类定义的版本变化了,所以读取老版本对象将可能不兼容新对象,因此系统抛出异常
读取为null:
Java里Serializable的那些事
3,保存的时候对象有serialVersionUID,然后删除这个字段,再次读取
Java里Serializable的那些事
则读取时serialVersionUID会被赋值一个自动生成的值
这个值根据类的成员变量方法定义等算出,增减成员变量或者增减方法都会导致这个值的改变
但是如果增加空格,交换成员变量位置等都不会导致这个值被改变
java.io.InvalidClassException: com.ye.test.model.Movie; Incompatible class (SUID): com.ye.test.model.Movie: static final long serialVersionUID =1L; but expected com.ye.test.model.Movie: static final long serialVersionUID =-6926675519878447654L;
也就是说,如果我们的Person类没有声明serialVersionUID变量,则以后这个类只要涉及到增减成员变量或者增减方法都会导致读取对象时不兼容。当然方法位置的调整不会影响。
4,如果先序列化保存一个对象到磁盘,然后修改这个类的定义,将其继承至一个基类(基类没有实现Serializable接口),则会报错
 java.io.InvalidClassException: com.ye.test.model.BaseMovie; IllegalAccessException
at java.io.ObjectStreamClass.resolveConstructorClass(ObjectStreamClass.java:692)
如果基类实现了Serializable接口,则能正常读取
5,如果基类已经实现了Serializable接口,则子类无需再次实现(当然子类再次实现也没关系)
6,父类的serialVersionUID成员变量对子类没有影响,即使他是public的,所以反序列化时能不能兼容只会跟子类的serialVersionUID对象数值相关。
也就是说开发中如果我们定义的类已经修改,需要告诉系统我们的类已经不再兼容老版本,则需要修改子类的serialVersionUID版本号而不是父类。
7,如果子类实现Serializable接口,父类没有实现,则只可以执行序列化,不能执行反序列化,执行反序列化时报错信息同4
总结:
1,如果存在继承关系,则只需在基类实现Serializable接口即可
2,serialVersionUID变量需在子类定义,父类定义没有意义(父类定义serialVersionUID用来消除编译警告信息也没关系)
3,如果没有定义serialVersionUID变量,则系统在保存或读取时会根据类结构自动算出一个值赋予serialVersionUID,如果类结构没有变化,那么这个值则是固定的,所以还是尽量使用自定义的值比较便于维护。
4,如果需求变更导致类结构调整,那么如果需要继续读取老版本对象的话, 则不要修改serialVersionUID的版本号,如果需要使用全新的数据而抛弃之前保存在磁盘里的对象,则修改serialVersionUID版本号,系统会默认读取为null。相当于之前没有保存过。
最后附上demo,有兴趣的话可以自己测试下。