序列化是一种将对象转换为字节流以便后续存储或传输的机制。在Java中,序列化随着平台在1.1版本中引入,成为API的一部分,这是由于分布式应用程序的普及和它们之间数据交换的需要。这一解决方案使得在JVM之间传递复杂的对象图和存储它们的状态变得简单透明。
序列化的问题在于必须严格保持数据的完整性,支持类的版本控制,并正确处理复杂结构(例如,带有循环引用的图)。并不是所有对象都可以序列化(例如,流,套接字,操作系统资源),因此序列化需要特别关注安全性。
解决方案是通过实现Serializable接口:
import java.io.*; class Person implements Serializable { private static final long serialVersionUID = 1L; private String name; private transient int age; // 不被序列化 public Person(String name, int age) { this.name = name; this.age = age; } }
通过ObjectOutputStream和ObjectInputStream,可以将对象写入文件并恢复:
Person p = new Person("Alice", 30); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser")); oos.writeObject(p); // 序列化 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser")); Person restored = (Person) ois.readObject();
关键特性:
Serializable接口。transient修饰符的字段不被序列化。可以序列化具有不可序列化字段的对象吗,即使它没有被使用?
回答:如果字段标记为transient,那么它根本不被序列化,过程不会因该字段类型不实现Serializable而抛出异常。如果字段不可序列化且不是transient,将抛出NotSerializableException。
如果类实现了Serializable,静态字段是否可序列化?
回答:不,静态字段不被序列化,因为它们属于类而不是实例。只有实例的状态被保存。
如果在序列化后更改类的结构(例如,添加或删除字段),会发生什么?
回答:如果指定了serialVersionUID版本,并且结构发生了不兼容的变化,反序列化时将出现InvalidClassException。为了确保兼容性,使用序列化提示和手动管理serialVersionUID。
工程师决定序列化对象,但没有添加serialVersionUID,并且没有将引用操作系统资源的字段声明为transient。更新应用程序和类后,出现了InvalidClassException,导致对象丧失完整性,并且反序列化不再工作。
优点:
缺点:
实现了带有显式serialVersionUID的可移植类,所有资源字段被声明为transient,手动序列化控制关键点。
优点:
缺点: