在 Java 中,序列化是将对象转换为字节序列的过程,以便于在网络上传输或保存到文件中,或者在程序之间传递。反序列化则是将字节序列转换回对象的过程。Java 提供了 ObjectInputStream
和 ObjectOutputStream
两个类来实现对象的序列化和反序列化。
1 ObjectOutputStream
:对象序列化
ObjectOutputStream
是 Java 中用于将对象序列化为字节流的类。它继承自 OutputStream
,可以将序列化后的字节序列写入到文件、网络等输出流中。
1.1 构造方法
ObjectOutputStream(OutputStream out)
该构造方法接收一个 OutputStream
对象作为参数,用于将序列化后的字节序列输出到指定的输出流中。例如:
FileOutputStream fos = new FileOutputStream("file.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);
1.2 序列化条件
一个对象要想被序列化,必须满足以下两个条件:
- 实现
Serializable
接口:该类必须实现java.io.Serializable
接口,否则会抛出NotSerializableException
。 - 所有字段可序列化:该类的所有字段都必须是可序列化的。如果一个字段不需要序列化,则需要使用
transient
关键字进行修饰。
例如:
public class Employee implements Serializable {public String name;public String address;public transient int age; // transient 修饰的字段不会被序列化
}
1.3 writeObject
方法
writeObject(Object obj)
方法是 ObjectOutputStream
类中用于将对象序列化成字节序列并输出到输出流中的方法。它可以处理对象之间的引用关系、继承关系、静态字段和 transient
字段。
示例:
public class ObjectOutputStreamDemo {public static void main(String[] args) {Person person = new Person("沉默王二", 20);try {FileOutputStream fos = new FileOutputStream("logs/person.dat");ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(person);oos.close();} catch (IOException e) {e.printStackTrace();}}
}class Person implements Serializable {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}
}
在上面的代码中,我们创建了一个 Person
对象,并使用 FileOutputStream
和 ObjectOutputStream
将其序列化并输出到 person.dat
文件中。
2 ObjectInputStream
:对象反序列化
ObjectInputStream
可以读取 ObjectOutputStream
写入的字节流,并将其反序列化为相应的对象。反序列化后的对象与序列化前的对象保持一致。
2.1 构造方法
ObjectInputStream(InputStream in)
该构造方法接收一个 InputStream
对象作为参数,用于从指定的输入流中读取字节序列并反序列化为对象。
2.2 readObject
方法
readObject()
方法用于从指定的文件输入流中读取对象并反序列化。示例如下:
String filename = "logs/person.dat"; // 待反序列化的文件名
try (FileInputStream fileIn = new FileInputStream(filename);ObjectInputStream in = new ObjectInputStream(fileIn)) {// 从指定的文件输入流中读取对象并反序列化Object obj = in.readObject();// 将反序列化后的对象强制转换为指定类型Person p = (Person) obj;// 打印反序列化后的对象信息System.out.println("Deserialized Object: " + p);
} catch (IOException | ClassNotFoundException e) {e.printStackTrace();
}
在上面的代码中,我们首先指定了待反序列化的文件名,然后创建了一个 FileInputStream
对象和一个 ObjectInputStream
对象。接着我们调用 ObjectInputStream
的 readObject
方法来读取指定文件中的对象,并将其强制转换为 Person
类型。最后我们打印了反序列化后的对象信息。
3 Kryo:高性能序列化库
尽管 Java 自带的序列化机制功能强大,但在实际开发中,很少使用 JDK 自带的序列化和反序列化,主要原因如下:
- 可移植性差:Java 特有的,无法跨语言进行序列化和反序列化。
- 性能差:序列化后的字节体积大,增加了传输/保存成本。
- 安全问题:攻击者可以通过构造恶意数据来实现远程代码执行,从而对系统造成严重的安全威胁。
为了解决这些问题,我们可以使用 Kryo,一个高性能的 Java 序列化和反序列化库。Kryo 具有高性能、高效率和易于使用和扩展等特点,已经在 Twitter、Groupon、Yahoo 以及多个著名开源项目(如 Hive、Storm)中广泛使用。
3.1 引入 Kryo
首先,在 pom.xml
中引入 Kryo 依赖:
<dependency><groupId>com.esotericsoftware</groupId><artifactId>kryo</artifactId><version>5.4.0</version>
</dependency>
3.2 使用 Kryo
创建一个 Kryo
对象,并使用 register()
方法将对象进行注册。然后,使用 writeObject()
方法将 Java 对象序列化为二进制流,再使用 readObject()
方法将二进制流反序列化为 Java 对象。最后,输出反序列化后的 Java 对象。
示例:
public class KryoDemo {public static void main(String[] args) throws FileNotFoundException {Kryo kryo = new Kryo();kryo.register(KryoParam.class);KryoParam object = new KryoParam("沉默王二", 123);Output output = new Output(new FileOutputStream("logs/kryo.bin"));kryo.writeObject(output, object);output.close();Input input = new Input(new FileInputStream("logs/kryo.bin"));KryoParam object2 = kryo.readObject(input, KryoParam.class);System.out.println(object2);input.close();}
}class KryoParam {private String name;private int age;public KryoParam() {}public KryoParam(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "KryoParam{" +"name='" + name + '\'' +", age=" + age +'}';}
}
4 小结
本节我们介绍了 Java 的序列化机制,并推荐了一款高性能的 Java 类库 Kryo 来取代 JDK 自带的序列化机制。Kryo 已经在 Twitter、Groupon、Yahoo 以及多个著名开源项目(如 Hive、Storm)中广泛使用,具有高性能、高效率和易于使用和扩展等特点。通过使用 Kryo,我们可以有效解决 JDK 自带序列化机制的痛点,提升系统的性能和安全性。
5 思维导图
6 参考链接
Java 序列流:Java 对象的序列化和反序列化