一文读懂 Java 序列化与反序列化 Serializable 接口 Externalizable 接口 serialVersionUID 的作用 static、transient 修饰不想被序列化的变量

一文读懂 Java 序列化与反序列化
Serializable 接口
Externalizable 接口
serialVersionUID 的作用
static、transient 修饰不想被序列化的变量

Java 将创建出来的对象,存放在 JVM 的对内存中,只有在 JVM 运行的时候,这些对象才会存在,一旦 JVM 停止运行,这些对象的状态也就随之消失了。

但是在一些应用场景中,我们需要将这些对象进行持久化,并且需要在使用的时候能够重新读取对象信息,比如说在 RPC 调用的时候,需要将对象通过网络进行传输,此时就需要下将对象记性序列化进行传输,再将其反序列化进行处理。

序列化(Serialization)是指将对象的状态信息,转换成可以可以存储或传输的形式。在网络传输过程中,可以是字节或是XML等格式。

  • 序列化:Java对象转换为字节序列。
  • 反序列化:字节序列恢复为原先的Java对象。

Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。

Java提供了下面这些类来进行序列化和反序列化

java.io.Serializable
java.io.Externalizable
ObjectOutput
ObjectInput
ObjectOutputStream
ObjectInputStream

java.io.Serializable 是一个没有任何方法或者字段的接口类,仅用于标识这个类可以被序列化。如果要序列化的类有父类,要想同时将在父类中定义过的变量持久化下来,那么父类也应该集成 java.io.Serializable 接口。

Java在进行序列化之前,会检查类是否实现了Serializable接口,没有实现就会报Serializable 接口就会报 NotSerializableException 异常。

一文读懂 Java 序列化与反序列化
Serializable 接口
Externalizable 接口
serialVersionUID 的作用
static、transient 修饰不想被序列化的变量

往序列化的方法底层看到 java.io.ObjectOutputStream#writeObject0 接口,它对被序列化对象的类型进行了判断,不是字符串、数组、枚举,也没有实现 Serializable 的接口都会抛出 NotSerializableException 异常。

一文读懂 Java 序列化与反序列化
Serializable 接口
Externalizable 接口
serialVersionUID 的作用
static、transient 修饰不想被序列化的变量

Externalizable 接口

Externalizable继承了Serializable,该接口中定义了两个抽象方法:writeExternal()readExternal()。当使用Externalizable接口来进行序列化与反序列化的时候需要开发人员重写writeExternal()readExternal()方法。

serialVersionUID 的作用

反序列化的时候可能遇到 InvalidClassException 异常,说是序列化的时候字节码文件的 serialVersionUID 和本地类的 serialVersionUID 不一致。这是由于反序列化的时候对类进行了修改,比如加上一个 toString 方法打印对象信息。

使用 ObjectOutputStream、ObjectInputStream 进行序列化

public void serialize() {
    User user = new User("深页", 18, "杭州");
    try (
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.txt"));
    ) {
        oos.writeObject(user);
        System.out.println("Serialized data is saved");
    } catch (IOException e) {
        e.printStackTrace();
    }
}

public void deserialize() {
    try (
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt"));
    ) {
        User user = (User) ois.readObject();
        System.out.println(user);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

static、transient 修饰不想被序列化的变量

被 static 和 transient 修饰的字段是不会被序列化的:

  • 序列化保存的是对象的状态而非类的状态,所以会忽略 static 静态域;
  • 序列化某个类的对象时,不希望某个字段被序列化,可以用 transient 修饰变量