Java Serializable 接口
最后修改时间:2025 年 4 月 16 日
java.io.Serializable 接口是一个标记接口,用于启用 Java 中的对象序列化。 序列化将对象转换为字节流以进行存储或传输。 反序列化从这些字节流中重建对象。
类实现 Serializable 来表明它们的实例可以被序列化。 该接口没有要实现的方法。 序列化会自动处理原始类型和对象图。 它保留对象引用和循环引用。
Serializable 接口概述
序列化需要实现 Serializable 接口。 该过程使用 ObjectOutputStream 和 ObjectInputStream。 瞬态 (Transient) 字段从序列化中排除。 静态 (Static) 字段永远不会被序列化。
public interface Serializable {
// Marker interface - no methods
}
上面的代码显示了 Serializable 的简单声明。 尽管是空的,但它启用了强大的序列化功能。 当实现此接口时,JVM 会自动处理所有序列化机制。
基本序列化示例
此示例演示了简单对象的基本序列化。 Person 类实现了 Serializable。 我们序列化到一个文件,然后反序列化回一个对象。
import java.io.*;
class Person implements Serializable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person("John Doe", 30);
// Serialization
try (ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("person.ser"))) {
oos.writeObject(person);
System.out.println("Serialized: " + person);
} catch (IOException e) {
e.printStackTrace();
}
// Deserialization
try (ObjectInputStream ois =
new ObjectInputStream(new FileInputStream("person.ser"))) {
Person deserialized = (Person) ois.readObject();
System.out.println("Deserialized: " + deserialized);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
此示例显示了 Person 对象的完整序列化和反序列化。 序列化数据被写入 "person.ser"。 反序列化后,对象的状态与序列化前相同。 请注意,在读取对象时需要强制转换。
瞬态字段示例
transient 关键字将字段从序列化中排除。 这对于敏感数据或不应持久化的字段很有用。 在反序列化期间,瞬态字段被设置为默认值。
import java.io.*;
class Account implements Serializable {
private String username;
private transient String password; // Won't be serialized
private double balance;
public Account(String username, String password, double balance) {
this.username = username;
this.password = password;
this.balance = balance;
}
@Override
public String toString() {
return "Account{username='" + username + "', password='" +
(password == null ? "null" : "****") + "', balance=" + balance + "}";
}
}
public class Main {
public static void main(String[] args) {
Account account = new Account("johndoe", "secret123", 1000.50);
// Serialize
try (ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("account.ser"))) {
oos.writeObject(account);
System.out.println("Serialized: " + account);
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize
try (ObjectInputStream ois =
new ObjectInputStream(new FileInputStream("account.ser"))) {
Account deserialized = (Account) ois.readObject();
System.out.println("Deserialized: " + deserialized);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
此示例演示了 transient 字段在序列化期间的行为。 密码字段从序列化中排除。 反序列化后,密码字段为 null。 这可以保护敏感数据免于以序列化形式存储。
使用 readObject 和 writeObject 进行自定义序列化
为了更好地控制序列化,类可以实现自定义方法。 writeObject 和 readObject 允许自定义序列化逻辑。 这些方法必须是私有的并且具有特定的签名。
import java.io.*;
class Employee implements Serializable {
private String name;
private transient double salary; // Custom serialization
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject(); // Serialize non-transient fields
oos.writeDouble(salary * 1.1); // Custom serialization logic
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject(); // Deserialize non-transient fields
this.salary = ois.readDouble() / 1.1; // Custom deserialization
}
@Override
public String toString() {
return "Employee{name='" + name + "', salary=" + salary + "}";
}
}
public class Main {
public static void main(String[] args) {
Employee emp = new Employee("Alice Smith", 50000);
// Serialize
try (ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("employee.ser"))) {
oos.writeObject(emp);
System.out.println("Serialized: " + emp);
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize
try (ObjectInputStream ois =
new ObjectInputStream(new FileInputStream("employee.ser"))) {
Employee deserialized = (Employee) ois.readObject();
System.out.println("Deserialized: " + deserialized);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
此示例显示了瞬态字段的自定义序列化。 薪水被标记为瞬态,但仍使用自定义逻辑进行序列化。 在序列化期间,薪水增加 10%。 在反序列化期间,它减少 10%。 这演示了如何实现自定义序列化逻辑。
SerialVersionUID 用于版本控制
serialVersionUID 是序列化类的版本控制机制。 它确保序列化对象和类版本之间的兼容性。 如果未定义,JVM 会根据类结构生成一个。
import java.io.*;
class Product implements Serializable {
private static final long serialVersionUID = 1L; // Version identifier
private String name;
private double price;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "Product{name='" + name + "', price=" + price + "}";
}
}
public class Main {
public static void main(String[] args) {
Product product = new Product("Laptop", 999.99);
// Serialize
try (ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("product.ser"))) {
oos.writeObject(product);
System.out.println("Serialized: " + product);
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize
try (ObjectInputStream ois =
new ObjectInputStream(new FileInputStream("product.ser"))) {
Product deserialized = (Product) ois.readObject();
System.out.println("Deserialized: " + deserialized);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
此示例演示了使用 serialVersionUID 进行版本控制。 该常量有助于在类更改时保持兼容性。 如果在反序列化期间 UID 不同,则会发生 InvalidClassException。 始终为重要的可序列化类显式声明 serialVersionUID。
继承和序列化
继承的序列化行为需要特别考虑。 如果超类是可序列化的,则其字段会自动序列化。 如果不是,则必须在序列化期间手动处理其字段。
import java.io.*;
class Address {
private String city;
private String country;
public Address(String city, String country) {
this.city = city;
this.country = country;
}
@Override
public String toString() {
return city + ", " + country;
}
}
class Customer extends Address implements Serializable {
private String name;
private transient Address address; // Non-serializable superclass
public Customer(String name, String city, String country) {
super(city, country);
this.name = name;
this.address = new Address(city, country);
}
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
oos.writeObject(address.toString()); // Serialize superclass state
}
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject();
String addrStr = (String) ois.readObject();
String[] parts = addrStr.split(", ");
this.address = new Address(parts[0], parts[1]);
}
@Override
public String toString() {
return "Customer{name='" + name + "', address=" + address + "}";
}
}
public class Main {
public static void main(String[] args) {
Customer customer = new Customer("Bob", "New York", "USA");
// Serialize
try (ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("customer.ser"))) {
oos.writeObject(customer);
System.out.println("Serialized: " + customer);
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize
try (ObjectInputStream ois =
new ObjectInputStream(new FileInputStream("customer.ser"))) {
Customer deserialized = (Customer) ois.readObject();
System.out.println("Deserialized: " + deserialized);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
此示例显示了如何处理继承的序列化。 Address 超类不可序列化,因此我们手动处理它。 Customer 类为超类字段实现了自定义序列化。 这确保了所有必要的数据都得到正确地序列化和反序列化。
序列化集合
如果 Java 集合的元素是可序列化的,则可以序列化它们。 这包括 ArrayList、HashMap 和其他标准集合。 整个集合结构在序列化期间得以保留。
import java.io.*;
import java.util.ArrayList;
import java.util.List;
class Student implements Serializable {
private String name;
private int id;
public Student(String name, int id) {
this.name = name;
this.id = id;
}
@Override
public String toString() {
return "Student{name='" + name + "', id=" + id + "}";
}
}
public class Main {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("Alice", 101));
students.add(new Student("Bob", 102));
students.add(new Student("Charlie", 103));
// Serialize
try (ObjectOutputStream oos =
new ObjectOutputStream(new FileOutputStream("students.ser"))) {
oos.writeObject(students);
System.out.println("Serialized: " + students);
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize
try (ObjectInputStream ois =
new ObjectInputStream(new FileInputStream("students.ser"))) {
@SuppressWarnings("unchecked")
List<Student> deserialized = (List<Student>) ois.readObject();
System.out.println("Deserialized: " + deserialized);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
此示例演示了序列化 Student 对象的 ArrayList。 整个集合结构得以保留。 每个元素都必须实现 Serializable。 反序列化的集合与原始集合相同,保持所有元素和顺序。
来源
在本文中,我们介绍了 Java Serializable 接口的基本方面。 理解序列化对于在 Java 应用程序中持久化对象并在网络上传输它们至关重要。
作者
列出所有Java教程。