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教程。