Java之序列化与反序列化
ObjectOutputStream
对象中的数据,以流的形式,写入到文件中保存。这个过程称为写出对象,也叫对象的序列化
ObjectInputStream
在文件中,以流的形式,将对象读取出来。这个过程叫读取对象,也叫对象的反序列化
1. 对象的序列化与反序列化
由于对象中的数据是没有规律的,所有只提供了字节流的形式,没有字符流的形式
定义一个Person测试对象:
public class Person {
private String name;
private int age;
public Person() {}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
1.1. ObjectOutputStream
1.1.1. 构造方法
ObjectOutputStream(OutputStream out)
1.1.2. 将对象序列化写入文件 void writeObject(Object obj)
按照常规流程将对象序列化,发现报错查看报错信息,提示NotSerializableException
查看API手册中该异常的描述提示说需要一个Serializable接口
接着查看Serializable接口描述表明如果需要序列化,必须实现Serializable接口,否则会抛出异常,而且Serializable接口是没有任何抽象方法的,只需要implements一下就可以解决这个报错了!
因此Person类代码修改第一行如下:
public class Person implements Serializable{
再运行程序就没有异常报错了,且3个对象也序列化到了文件中
因此Serializable接口是一个标记性接口(没有任何抽象方法,但必须要implements一下)
1.2. ObjectInputStream
1.2.1. 构造方法
ObjectInputStream(InputStream in)
1.2.2. 读文件将对象反序列化到对象
必须要抛出CassNotFoundException异常,因为反序列化必须要有反序列对象的class文件,在本例中是Person.class文件
1.3 序列化、反序列化的常见问题
1.3.1 自定义的类的静态成员不能做序列化
这段测试代码,运行之前,先将Person中的age采用static修饰:private static int age;
运行程序,先调用objStreamWriteDemo(), 再调用objStreamReadDemo()
从结果来看,kluter的age显示的是leslin的age
原因是静态成员是类的,不属于对象。而对象序列化反序列化针对的是对象,不是类
1.3.2 瞬态关键字:transient
在1.3中静态成员的值不能够被序列化
使用关键字transient修饰age,阻止对象中的age成员的序列化
private transient int age;
1.3.3 序列化中序列号的冲突问题
在基本的Person类中,如果age成员先用private修饰,序列化到文件中
接着将Person的age成员用public修饰,运行从文件中反序列化时,程序会报错,报错第一行:
Exception in thread "main" java.io.InvalidClassException: com.gamebear.s1.Person; local class incompatible: stream classdesc serialVersionUID = 8738724770247792658, local class serialVersionUID = 4187457991494988290
可以看出是由于序列化之后生成的序列号,和反序列化生成的序列号产生了冲突。
当Person类加入implements Serializable时,eclispse编译器会自动帮你将java文件编程成class文件,在class文件中保存了这个序列号,且这个序列号只会在类序列化时才会生成
这样会导致在序列化时是一个序列号并写入了序列化文件中,而反序列化时是另一个序列号,从序列化文件读取出来的序列号和新的类的序列号进行对比如果相同则读取成功,如果不同则读取失败,报上面的错误
为了解决序列号冲突的问题,我们需要自定义序列号
在Person类中成员变量前加入一行, 且等号后面的数值可以随便改一个:
private static final long serialVersionUID = 8738724770247792658L;
这样即便是反序列化前修改了成员的属性也能读取成功了