Java序列化


Java提供了一种称为对象序列化的机制,该机制可以将对象表示为字节序列,其中包括对象的数据以及有关对象的类型和存储在对象中的数据类型的信息。

将序列化的对象写入文件后,可以从文件中读取并反序列化为对象,即表示对象及其数据的类型信息和字节可用于在内存中重新创建对象。

最令人印象深刻的是,整个过程是与JVM无关的,这意味着可以在一个平台上序列化一个对象,并在一个完全不同的平台上反序列化一个对象。

ObjectInputStreamObjectOutputStream是高级流,其中包含用于序列化和反序列化对象的方法。

ObjectOutputStream类包含许多用于写入各种数据类型的写入方法,但其中一种方法尤其引人注目:

public final void writeObject(Object x) throws IOException

上面的方法序列化一个Object并将其发送到输出流。同样,ObjectInputStream类包含以下用于反序列化对象的方法:

public final Object readObject() throws IOException, ClassNotFoundException

此方法从流中检索下一个Object并将其反序列化。返回值为Object,因此需要将其强制转换为适当的数据类型。

为了演示序列化在Java中的工作方式,我将使用我们在本书前面讨论的Employee类。假设我们具有以下Employee类,该类实现了Serializable接口:

public class Employee implements java.io.Serializable {
    public String name;
    public String address;
    public transient int SSN;
    public int number;
   
    public void mailCheck() {
        System.out.println("Mailing a check to " + name + " " + address);
    }
}

请注意,要成功地对一个类进行序列化,必须满足两个条件:

  • 该类必须实现java.io.Serializable接口;

  • 该类中的所有字段都必须可序列化。如果字段不可序列化,则必须将其标记为transient.

如果想知道Java标准类是否可序列化,请查看该类的文档。测试很简单:如果该类实现java.io.Serializable,则它是可序列化的;否则,它可以序列化。否则,不是。

序列化对象


ObjectOutputStream类用于序列化对象。下面的SerializeDemo程序实例化一个Employee对象,并将其序列化为文件。

程序执行完毕后,将创建一个名为employee.ser的文件。该程序不会产生任何输出,但会研究代码并尝试确定程序正在执行的操作。

注意:将对象序列化为文件时,Java中的标准约定是给文件名一个.ser后缀。

import java.io.*;
public class SerializeDemo {

    public static void main(String [] args) {
        Employee e = new Employee();
        e.name = "Reyan Ali";
        e.address = "Phokka Kuan, Ambehta Peer";
        e.SSN = 11122333;
        e.number = 101;
      
        try {
            FileOutputStream fileOut =
            new FileOutputStream("/tmp/employee.ser");
            ObjectOutputStream out = new ObjectOutputStream(fileOut);
            out.writeObject(e);
            out.close();
            fileOut.close();
            System.out.printf("Serialized data is saved in /tmp/employee.ser");
        } catch (IOException i) {
            i.printStackTrace();
        }
    }
}

反序列化对象


以下DeserializeDemo程序反序列化SerializeDemo程序中创建的Employee对象,研究该程序并尝试确定其输出:

import java.io.*;
public class DeserializeDemo {

    public static void main(String [] args) {
        Employee e = null;
        try {
            FileInputStream fileIn = new FileInputStream("/tmp/employee.ser");
            ObjectInputStream in = new ObjectInputStream(fileIn);
            e = (Employee) in.readObject();
            in.close();
            fileIn.close();
        } catch (IOException i) {
            i.printStackTrace();
            return;
        } catch (ClassNotFoundException c) {
            System.out.println("Employee class not found");
            c.printStackTrace();
            return;
        }
      
        System.out.println("Deserialized Employee...");
        System.out.println("Name: " + e.name);
        System.out.println("Address: " + e.address);
        System.out.println("SSN: " + e.SSN);
        System.out.println("Number: " + e.number);
    }
}

这将产生以下结果:

Deserialized Employee...
Name: Reyan Ali
Address:Phokka Kuan, Ambehta Peer
SSN: 0
Number:101

以下是要注意的重要事项:

  • try / catch块需要捕获ClassNotFoundException,该异常由readObject()方法声明。为了使JVM能够反序列化对象,它必须能够找到该类的字节码。如果JVM在对象反序列化期间找不到类,则会抛出ClassNotFoundException。

  • 请注意,将readObject()的返回值强制转换为Employee引用。

  • 序列化对象时,SSN字段的值为11122333,但是由于该字段是transient,因此未将该值发送到输出流。反序列化的Employee对象的SSN字段为0。