“你会记住今天我们研究了将对象保存到文件以及从文件读取对象这件事吗?”

“是的,我们刚刚完成了保存到输出流并从输入流读取的操作。”

“做得不错,阿米戈。很高兴听到你注意到这些细节。你能编写代码,使其保存到文件并从文件读取吗?”

“完成什么?!声明 FileInputStream 和 FileOutputStream 并将其传递给 save 和 load 方法。这没什么难的。超级简单。”

“我很欣慰。现在我们开始一个新的主题:序列化。”

序列化与我们刚才所做的几乎一样,但是酷多了,并且直接内置在 Java 机器中。Java 机器可以存储和加载其对象。它甚至不需要使用 save 和 load 方法即可完成:所有对象都存储在 Java 机器内部,它拥有对这些对象的完全访问权限。”

我们只需选取对象,将其保存到流并从流中读取它:

代码
public static void main(String[] args) throws Exception
{
 Cat cat = new Cat();

 //Save a cat to file
 FileOutputStream fileOutput = new FileOutputStream("cat.dat");
 ObjectOutputStream outputStream = new ObjectOutputStream(fileOutput);
 outputStream.writeObject(cat);
 fileOutput.close();
 outputStream.close();

 //Load a cat from file
 FileInputStream fiStream = new FileInputStream("cat.dat");
 ObjectInputStream objectStream = new ObjectInputStream(fiStream);
 Object object = objectStream.readObject();
 fiStream.close();
 objectStream.close();

 Cat newCat = (Cat)object;
}

“完了?”

“没错。有一个非常复杂的大型序列化机制,它允许我们保存到流并从几乎任意数据类型的流中读取。”

“几乎任意类型。而不是任意数据类型?”

“是的,事实是并非所有对象都天生具有被保存的能力。有些对象不在内部存储其所有数据。相反,它们只引用其他对象和/或数据源。例如,控制台 (System.in)、输入流 (InputStream) 等等。”

这就是 Java 创建者提出特殊的 Serializable 接口标记的原因。之所以称为标记,是因为它不包含任何数据和方法,而只用于“标记”类。如果认为我们的类在内部存储其所有数据,则可以使用 implements Serializable 来标记它。

下面是一个支持序列化的“cat”示例:

代码
class Cat implements Serializable
{
 public String name;
 public int age;
 public int weight;
}

当我们尝试序列化(保存)对象时,Java 机器会检查它是否支持序列化:它是否实现了 Serializable 接口?如果是,则保存对象。如果否,则抛出异常以指示无法进行序列化。
在此,你需要明白可序列化对象只能由可序列化对象组成。

“嗯,有道理。不能只保存整体,不保存部分。”

“完全正确。”

“那么 int、String 和 ArrayLists 呢?”

“它们都支持序列化。Java 创建者特别注意要确保做到这一点。这里不能有任何问题。”

而且,当对象被序列化时,将保存对象的类型。现在,你可以在 Object 变量中保存对 Cat 对象的引用。如果一切都能序列化和反序列化就好了。

“反序列化”

"反序列化是逆转序列化的过程:从流/文件读取和重构对象。”

“啊,现在没有其他问题了。”