序列化和反序列化

什么是序列化和反序列化呢?

序列化就是将对象转成字节序列的过程。

反序列化就是将字节序列重组成对象的过程。

为什么要有对象序列化机制

程序中的对象,都是存放在内存中的。当我们关闭程序或内存掉电后,无论如何它都不会继续存在了。那么有没有一种机制能让对象具有“持久性”呢?

序列化机制提供了一种方法,你可以将对象序列化的字节流输入到文件,并保存在磁盘或数据库上。

序列化机制的另外一个应用场景是,我们可以通过网络传输对象。Java中的远程方法调用(RMI),底层就需要序列化机制的保证。

序列化机制从某种意义上,也弥补了不同平台带来的差异。毕竟转换后的字节流可以在其他平台上进行反序列化来恢复对象。


Java中的序列化和反序列化

假设我们需要将一个Student类的对象进行序列化。无论是用于持久化还说网络传输都可以。

import java.io.Serializable;

public class StudentClass implements Serializable {

    //...类属性和方法。

}

需要被序列化的类都必须实现Serializable接口。但Serializable接口实际上是一个空接口,里面并没有包含任何接口方法。但这并不代表Student类实现Serializable接口是没必要的。

在这里Serializable起到一个标记作用。它告诉底层代码,该类是可以被序列化的。但真正序列化的代码并不是由Serializable完成。

如果对一个没有实现Serializable接口的类进行序列化会抛出java.io.NotSerializableException异常。

还有一个非常重要的是就是序列化ID:serialVersionUID。

在定义一个可序列化的类时,如果没有显式地定义一个serialVersionUID字段,则Java运行时环境会根据该类的各方面信息自动地为它生成一个默认的serialVersionUID。一旦更改了类的结构或信息,则类的serialVersionUID也会跟着变化。

在反序列化时,JVM会把字节流中的serialVersionUID和被序列化类中的serialVersionUID进行对比。只有两者一致,才能重写反序列化。否则就会报java.io.InvalidClassException异常。

因此,为了serialVersionUID的确定性。建议写代码时,凡是实现了Serializable的可序列化类,都最好显式地声明一个明确的serialVersionUID值。

import java.io.Serializable;

public class StudentClass implements Serializable {

    private static final long serialVersionUID = -4963266899668807475L;

    //...类属性和方法。

}

如果不想手动赋值,可以使用IDE的自动添加功能。例如IDEA可以参考:IDEA如何自动生成serialVersionUID

其中还有两种特殊情况:

  • 凡是被static修饰的字段是不会被序列化的。
  • 凡是被transient修饰的字段也是不会被序列化的。

参考:

7000字带你死磕Java I/O流知识

序列化/反序列化,我忍你很久了