如何对Java对象实例进行序列化和反序列化

357 阅读5分钟

序列化是将一个Java对象实例转换为一系列字节的过程。然后你可以把序列化的对象写进一个文件,或把它保存到数据库中供以后使用。

反序列化是序列化的反面,这意味着你把Java对象的字节转换成Java对象实例,允许你读取序列化对象中包含的信息。

对Java对象进行序列化和反序列化的能力使你能够将信息从一个Java虚拟机传到另一个。

本教程将帮助你学习如何对Java对象进行序列化和反序列化。让我们先从序列化一个对象开始。

如何序列化一个Java对象

要序列化一个Java对象,你用来实例化该对象的类必须实现Serializable 接口。

这里有一个例子:

import java.io.Serializable;
class Car implements Serializable {
String name = "Tesla Model S";
String owner = "Nathan";
int year = 2019;
}

大多数内置的Java类如ArrayListHashSet 已经实现了Serializable 类,所以你也可以将它们序列化。

要开始序列化过程,你需要首先从该类中创建一个新的对象:

Car myCar = new Car();

接下来,你需要从java.io 包中创建一个FileOutputStreamObjectOutputStream 类的新实例。

FileOutputStream 创建一个输出流,使你能够将数据写入一个计算机文件,并将该文件保存在Java应用程序之外。你需要把将由该对象创建的新文件的String name 作为其参数:

FileOutputStream fos = new FileOutputStream("serializable.ser");

ObjectOutputStream 允许你转换和写入对象数据到一个输出流。在你创建了上面的FileOutputStream 实例fos ,将该流传入ObjectOutputStream

同时,用一个try..catch 块来包装这两个类的实例化,以处理它们的异常:

import java.io.*;
Car myCar = new Car();
try{
FileOutputStream fos = new FileOutputStream("serializable.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos);
} catch (Exception e) {
System.out.println(e);
}

最后,调用ObjectOutputStream.writeObject() 方法,并将myCar 对象作为其参数,如下所示:

Car myCar = new Car();
try{
FileOutputStream fos = new FileOutputStream("serializable.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos);
 oos.writeObject(myCar);
} catch (Exception e) {
System.out.println(e);
}

之后,你可以调用flush()close() 方法来关闭流并释放写入过程中使用的内存。

flush() 方法确保在关闭流之前,Java已经完成了将序列化对象写入文件。如果你的流还没有完成写入,那么close() 方法就不会被执行:

Car myCar = new Car();
try{
FileOutputStream fos = new FileOutputStream("serializable.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(myCar);
 oos.flush();
 oos.close();
 System.out.println("Serialization success");
} catch (Exception e) {
System.out.println(e);
}

当你在你的main() 方法中运行上述代码时,在你的Java项目根文件夹中应该创建一个名为serializable.ser 的新文件:

MyJavaProject
├── MyJavaProject.iml
├── out/
 ├── serializable.ser
 └── src/

请注意,一个序列化的对象文件只能从Java应用程序内部读取。使用文本编辑器程序打开.ser 文件,只会显示没有意义的文本和符号。 下面是序列化Car 类的完整代码:

public class Main {
public static void main(String[] args) {
try {
Car myCar = new Car();
FileOutputStream fos = new FileOutputStream("serializable.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(myCar);
oos.flush();
oos.close();
System.out.println("Serialization success");
} catch (Exception e) {
System.out.println(e);
}
}
}
class Car implements Serializable {
String name = "Tesla Model S";
String owner = "Nathan";
int year = 2019;
}

现在你已经学会了如何将一个对象序列化成一个文件,现在是时候学习如何反序列化和读取文件内容了。

如何反序列化一个Java对象文件

为了反序列化和读取一个对象文件的内容,你需要创建FileInputStreamObjectInputStream 的新实例。

你需要把文件名传给FileInputStream 构造函数,就像上面的FileOutputStream

FileInputStream fis = new FileInputStream("serializable.ser");

然后将fis 对象传入ObjectInputStream 构造函数:

ObjectInputStream ois = new ObjectInputStream();

最后,调用ObjectInputStream.readObject() 方法,从输入流中返回一个对象。

你需要把返回的对象转换成Car ,如下图所示:

Car readCar = (Car) ois.readObject();

有了这个,你就可以打印出readCar 对象的属性,如下图所示:

Car readCar = (Car) ois.readObject();
System.out.println("Car name:" + readCar.name);
System.out.println("Car owner:" + readCar.owner);
System.out.println("Car production year:" + readCar.year);

一旦你完成了对对象的访问,你就可以调用ObjectInputStream.close() 方法来关闭输入流并释放这个过程中使用的内存。

反序列化一个对象的完整代码如下:

public class Main {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("serializable.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
Car readCar = (Car) ois.readObject();
System.out.println("Car name:" + readCar.name);
System.out.println("Car owner:" + readCar.owner);
System.out.println("Car production year:" + readCar.year);
ois.close();
} catch (Exception e) {
System.out.println(e);
}
}
}

这就是你如何反序列化和读取一个序列化对象文件的内容。

你也可以在GitHub Gist上获取一份源代码。

结论

对Java对象进行序列化和反序列化的能力允许你在实例化后保存对象实例的属性。

假设你有一个动态的Car 类,其构造函数如下:

class Car implements Serializable {
String name;
String owner;
 Car(String name, String owner){
 this.name = name;
 this.owner = owner;
 }
}

然后,你可以将Car 类的每个实例以不同的nameowner 的值序列化到一个文件中,以后需要时再读取它们。

你需要调用writeObject() 方法的次数与你拥有的对象实例一样多:

Car myCar = new Car("Tesla Model S", "Nathan");
Car myOtherCar = new Car("Mercedes-Benz EQS", "Nathan");
FileOutputStream fos = new FileOutputStream("serializable.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(myCar);
oos.writeObject(myOtherCar);

那么当你在一个序列化的文件中有两个对象时,你需要在读取过程中调用两次readObject() 方法:

FileInputStream fis = new FileInputStream("serializable.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
Car readCar = (Car) ois.readObject();
System.out.println("Car name:" + readCar.name);
System.out.println("Car owner:" + readCar.owner);
Car readOtherCar = (Car) ois.readObject();
System.out.println("Car name:" + readOtherCar.name);
System.out.println("Car owner:" + readOtherCar.owner);

你刚刚学会了在Java中如何进行序列化和反序列化。干得好!😉