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

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

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

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

ObjectOutputStream 类包含许多写入方法,用于编写各种对象数据类型,但有一个方法特别突出

public final void writeObject(Object x) throws IOException 

上述方法序列化一个对象并将其发送到输出流。类似地,ObjectInputStream 类包含以下用于反序列化对象的方法

public final Object readObject() throws IOException, ClassNotFoundException 

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

为了演示序列化在 Java 中的工作原理,我将使用我们在本书前面讨论过的 Employee 类。假设我们有以下 Employee 类,它实现了 Serialized 接口

示例

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.Serialized 接口。

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

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

序列化对象

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

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

注意 将对象序列化到文件时,标准约定Java是给文件赋予.ser扩展名。

示例

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

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("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();
      }
   }
}
class Employee implements java.io.Serializable {
   private static final long serialVersionUID = 1L;
   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);
   }
} 

输出

Serialized data is saved in employee.ser 

反序列化对象

以下 DeserializeDemo 程序反序列化在先前程序中创建的 Employee 对象。研究程序并尝试确定其输出

示例

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class DeserializeDemo {

   public static void main(String [] args) {
      Employee e = null;
      try {
         FileInputStream fileIn = new FileInputStream("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);
   }
}
class Employee implements java.io.Serializable {

   private static final long serialVersionUID = 1L;
   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);
   }
} 

输出

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

以下是需要注意的要点

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

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

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