(四)、Java I/O系统——序列化和反序列化
当创建对象后,程序运行时对象就会存在,但是程序停止时,对象也就消失了。如果希望对象在程序不运行的情况下仍然能够保存其信息,例如服务器端的session信息的保存,可以使用对象的序列化。本节主要学习使用ObjectOutputStream和ObjectInputStream实现对象的序列化以及反序列化,对对象进行存储和恢复。
- 序列化:将内存中的对象写入到文件设备中
- 反序列化:将文件设备中的对象数据转换为内存对象
获取ObjectInputStream、ObjectOutputStream允许对对象进行输入输出:
ObjectOutputStream ouput = new ObjectOutputStream(new FileOutputStream("obj.dat)));
ObjectInputStream input = new ObjectInputStream(new FileInputStream("obj.dat")))
对象的序列化
package com.xiaopeng.object;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
class Book {
private String name;
private int pages;
private double price;
public Book(String name, int pages, double price) {
this.name = name;
this.pages = pages;
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"name" + name +
"pages=" + pages +
", price=" + price +
'}';
}
}
public class MyObjectOuputStream {
public static void main(String[] args) {
ObjectOutputStream output = null;
try {
Book book = new Book("java设计模式", 564, 76.9);
output = new ObjectOutputStream(new FileOutputStream("./src/obj.dat"));
output.writeObject(book);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (output != null) {
try {
output.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
发现会出现如错误:
Exception in thread “main” java.lang.RuntimeException: java.io.NotSerializableException: com.xiaopeng.object.Book
at com.xiaopeng.object.MyObjectOuputStream.main(MyObjectOuputStream.java:38)
Caused by: java.io.NotSerializableException: com.xiaopeng.object.Book
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at com.xiaopeng.object.MyObjectOuputStream.main(MyObjectOuputStream.java:36)
类必须实现java.io.Serializable接口才可以进行序列化以及反序列化。将上述例子中的Book类实现java.io.Serializable接口,就可以对book对象进行序列化,将它的信息写入obj.dat中。存储一个可序列化对象时,会对该对象的类进行编码,编码包括类名、类的签名、对象实例变量的值以及初始对象引用的其他对象闭包,但是不包括对象静态变量的值。
对象的反序列化
使用ObjectInputStream将存储在文件设备上的对象信息读取到对象输入流的操作称为对象的反序列化。
public static void main(String[] args){
ObjectInputStream input = null;
try {
input = new ObjectInputStream(new FileInputStream("./src/obj.dat"));
Book book = (Book) input.readObject();
System.out.println(book);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if(input != null){
try {
input.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
输出:
Book{name=java设计模式, pages=564, price=76.9}
还需要注意以下两点:
- 对于那些不需要进行序列化的成员变量(加入某个类的成员变量不是基本类型,也没有实现java.io.Serializable接口),使用 transient 修饰符进行修饰即可。
- 在对象序列化时会将serialVersionUID写入文件中,该UID是通过类中可序列化成员的数字签名运算出来的一个long型的值。只要这些成员没有改变,那么每次运行的返回值都一样。在对象反序列化时会将文件中的UID与Class对象的UID进行比较,如果两者不相等就会抛出如下错误:
Caused by: java.io.InvalidClassException: com.xiaopeng.object.Book; local class incompatible: stream classdesc serialVersionUID = 6218191536271159894, local class serialVersionUID = 4161807747659598353
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:687)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1876)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1745)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2033)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1567)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427)
at com.xiaopeng.object.MyObjectInputStream.main(MyObjectInputStream.java:13)
如果被序列化的对象需要被不同版本的类兼容(假如:被序列化的类的成员会发生改变),在序列化对象之前需要指定该类的serialVersionUID,方法如下:
class Book implements Serializable {
private static final long serialVersionUID = 4161804765954398353L;
private String name;
private int pages;
private double price;
//..........
}
在指定了serialVersionUID之后,即使对该类进行修改,仍然可以正常反序列化。