Java I/O 系列文章目录:

 

今天我们来介绍下,Java 中对象的序列化和反序列化。

Java 提供了 ObjectOutputStream ObjectInputStream Serializable 三个类来实现序列化和反序列化功能。

首先,把对象序列化必须实现 Serializable 接口,只有这样才能把对象持久化到硬盘或者其他地方。

ObjectOutputStream 也是字节输出流的子类,继承关系如下所示:

java.lang.Object
  java.io.OutputStream
      java.io.ObjectOutputStream
      
      
ObjectInputStream 是字节输入流的子类,继承关系如下图所示:

java.lang.Object
  java.io.InputStream
      java.io.ObjectInputStream

对象序列化操作需要使用到 ObjectOutputStream 和 ObjectInputStream 两个对象

序列化操作:
     ObjectOutputStream.writeObject(Object obj)

反序列化操作:
     ObjectInputStream.readObject()

通过一个简单的程序测试:

 

public static void objetcSeri()throws Exception {
    ObjectOutputStream oos =new ObjectOutputStream(
    new FileOutputStream("person.object"));
    //写入person对象
    oos.writeObject(new Person("johnny",21,"CHINA"));
    oos.close();
}

控制台 出现如下错误:

Exception in thread "main" java.io.NotSerializableException: com.day21.Person
	at java.io.ObjectOutputStream.writeObject0(Unknown Source)
	at java.io.ObjectOutputStream.writeObject(Unknown Source)
	at com.day21.ObjectSerializable.objetcSeri(ObjectSerializable.java:15)
	at com.day21.ObjectSerializable.main(ObjectSerializable.java:9)

从错误输出可以看出,Person这个类没有实现 Serializable 接口,更正错误后发现硬盘中多了一个 person.object 文件

其内容为一些我们看不懂的字符:


 

java对象序列化比较 java对象序列化工具_ObjectOutputStream

那么我们是否通过 ObjectInputStream 读取到文件的内容呢?

public static void readObject() throws Exception {
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.object"));
    Person person = (Person)ois.readObject();
    System.out.println(person);
}

控制台输出结果: name:johnny age:21 country:CHINA 

我们来看一下 Serializable 接口的官方文档描述知道:如果实现了序列化接口的类没有显示声明 serialVersionUID 变量,序列化运行时将计算一个默认的 serialVersionUID 的值为这个类基于类的变量方面的值

我们强烈建议所有实现序列化接口的类都要显示的声明 serialVersionUID 变量,因为默认的 serialVersionUID 变量值对于类的修改是非常敏感的

因为他的值就是根据类成员的签名而生成的而不是系统随机生成的,

假设我们对类A进行序列化,在一般情况下我们可以反序列化得到类 A 的信息,如果我们一旦修改了类 A,那么我们再次反序列化就会出现 java.io.InvalidClassException 异常的,

因为第一次编译类 A 的时候它的 serialVersionUID 是一个值,你修改类 A 后在再次编译 serialVersionUID 的值已经变了。

因此为了保证在不同的编译器 serialVersionUID 变量的值一致性, 所以建议把该变量定义成一个 private 的常量.

下面来模拟这种情况,现在我修改Person类,如下图所示:

 

 

java对象序列化比较 java对象序列化工具_Java 序列化和反序列化_02

 

重新反序列化一次:

public static void readObject() throws Exception {
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.object"));
    Person person = (Person)ois.readObject();
    System.out.println(person);
}

控制台打印店异常: 

Exception in thread "main" java.io.InvalidClassException: com.huaxia.day21.Person; local class incompatible: 
    stream classdesc serialVersionUID = -379036032685453711, 
    local class serialVersionUID = 1208026685571330753

总结: 对象持久化需要实现 Serializable 接口, 并且声明 serialVersionUID 常量