在 Java 中,序列化(Serialization)和反序列化(Deserialization)是两个重要的概念,它们主要用于将对象转换为字节流(序列化)以及将字节流恢复为对象(反序列化)。这在对象的存储、网络传输等场景中非常有用。如下图:
序列化是指将一个 Java 对象转换为字节序列的过程,这个字节序列可以被保存到文件、数据库,或者通过网络传输到其他地方。反序列化则是序列化的逆过程,它将字节序列恢复为原来的 Java 对象。
序列化的主要用途包括:
对象持久化:将对象保存到文件、数据库中,以便后续恢复使用。
远程方法调用(RMI):在分布式系统中,通过网络传输对象。
对象复制:通过序列化和反序列化可以实现对象的深拷贝。
要使一个类的对象能够被序列化,该类必须实现 java.io.Serializable 接口。Serializable 接口是一个标记接口,它没有任何方法,只是用来表明实现该接口的类可以被序列化。
以下是一个简单的可序列化类的示例:
package com.hxstrive.java_io.serializable; import java.io.Serializable; /** * 实现 Serializable 接口 */ public class Person implements Serializable { private static final long serialVersionUID = 1L; 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; } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } }
在上述代码中,Person 类实现了 Serializable 接口,并且定义了一个 serialVersionUID 常量。serialVersionUID 用于在反序列化时验证序列化对象的版本一致性,如果不指定,Java 会根据类的结构自动生成一个。
要将一个对象序列化,可以使用 ObjectOutputStream 类。以下是一个示例:
package com.hxstrive.java_io.serializable; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class SerializationExample { public static void main(String[] args) { Person person = new Person("John", 30); try (FileOutputStream fos = new FileOutputStream("person.ser"); ObjectOutputStream oos = new ObjectOutputStream(fos)) { // 写入对象 oos.writeObject(person); System.out.println("对象已序列化"); } catch (IOException e) { e.printStackTrace(); } } }
运行示例,输出:
在上述代码中,首先创建了一个 Person 对象,然后使用 FileOutputStream 和 ObjectOutputStream 将该对象写入到名为 person.ser 的文件中。
要将一个序列化的对象恢复为原来的对象,可以使用 ObjectInputStream 类。以下是一个示例:
package com.hxstrive.java_io.serializable; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; public class DeserializationExample { public static void main(String[] args) { final String userDir = System.getProperty("user.dir"); try (FileInputStream fis = new FileInputStream(userDir + File.separator + "person.ser"); ObjectInputStream ois = new ObjectInputStream(fis)) { // 读取对象 Person person = (Person) ois.readObject(); System.out.println("对象已反序列化: " + person); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } }
运行结果:
对象已反序列化: Person{name='John', age=30}
在上述代码中,使用 FileInputStream 和 ObjectInputStream 从 person.ser 文件中读取对象,并将其转换为 Person 类型。
如果一个类的某个字段被声明为 transient,那么在序列化时该字段将被忽略。例如:
import java.io.Serializable; public class Person implements Serializable { private static final long serialVersionUID = 1L; private String name; private transient int age; // 该字段不会被序列化 // 构造方法、getter 和 setter 方法 }
在进行序列化和反序列化时,建议显式地指定 serialVersionUID,以确保在类的结构发生变化时,仍然能够正确地进行反序列化。