一文读懂 Java 序列化与反序列化

序列化与反序列化

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

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

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

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

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

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

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

Serializable 接口

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

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

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

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 修饰变量
原文地址:https://www.cnblogs.com/shuiyj/p/13161177.html