Java ObjectInputStream
外观
Java ObjectInputStream 全面指南[编辑 | 编辑源代码]
ObjectInputStream 是Java I/O系统中用于反序列化对象的核心类,它属于java.io
包,与ObjectOutputStream配合使用实现对象的持久化存储和网络传输。
核心概念[编辑 | 编辑源代码]
ObjectInputStream的主要功能是从输入流中读取被序列化的Java对象,并将其还原为内存中的对象实例。这个过程称为反序列化(Deserialization)。
序列化与反序列化[编辑 | 编辑源代码]
数学表示:
基础用法[编辑 | 编辑源代码]
类声明[编辑 | 编辑源代码]
public class ObjectInputStream
extends InputStream
implements ObjectInput, ObjectStreamConstants
基本示例[编辑 | 编辑源代码]
从文件读取对象:
try (FileInputStream fis = new FileInputStream("data.obj");
ObjectInputStream ois = new ObjectInputStream(fis)) {
// 读取对象
Object obj = ois.readObject();
if (obj instanceof Person) {
Person p = (Person) obj;
System.out.println("反序列化对象: " + p);
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
假设Person
类已实现Serializable
接口,输出可能是:
反序列化对象: Person{name='John', age=30}
关键方法详解[编辑 | 编辑源代码]
方法 | 描述 |
---|---|
readObject() |
读取并反序列化对象 |
readInt() |
读取32位整数 |
readUTF() |
读取UTF-8格式字符串 |
defaultReadObject() |
使用默认机制反序列化非静态/瞬态字段 |
readFields() |
读取序列化字段 |
自定义反序列化[编辑 | 编辑源代码]
通过实现private void readObject(ObjectInputStream ois)
方法:
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
// 默认反序列化
ois.defaultReadObject();
// 自定义处理
this.timestamp = System.currentTimeMillis();
}
安全注意事项[编辑 | 编辑源代码]
重要警告:反序列化不受信任的数据可能导致严重安全问题:
- 远程代码执行(RCE)
- 拒绝服务攻击(DoS)
防护措施:
1. 使用ObjectInputFilter
(Java 9+)
2. 验证序列化UID
3. 避免反序列化不可信来源数据
过滤器示例[编辑 | 编辑源代码]
ObjectInputFilter filter = info -> {
if (info.serialClass() != null &&
info.serialClass().getName().equals("com.example.Person")) {
return ObjectInputFilter.Status.ALLOWED;
}
return ObjectInputFilter.Status.REJECTED;
};
ois.setObjectInputFilter(filter);
高级特性[编辑 | 编辑源代码]
版本控制[编辑 | 编辑源代码]
使用serialVersionUID
保持版本兼容:
private static final long serialVersionUID = 1L;
替代机制[编辑 | 编辑源代码]
对于复杂场景,可考虑:
Externalizable
接口- JSON/XML序列化库(如Jackson、Gson)
实际应用案例[编辑 | 编辑源代码]
分布式系统通信[编辑 | 编辑源代码]
持久化存储[编辑 | 编辑源代码]
// 保存游戏状态
public void saveGame(Player player, String filename) {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(filename))) {
oos.writeObject(player);
} catch (IOException e) {
System.err.println("保存失败: " + e.getMessage());
}
}
// 加载游戏
public Player loadGame(String filename) {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(filename))) {
return (Player) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
System.err.println("加载失败: " + e.getMessage());
return null;
}
}
常见问题[编辑 | 编辑源代码]
反序列化失败原因[编辑 | 编辑源代码]
1. 类未实现Serializable
2. serialVersionUID不匹配
3. 类定义发生不兼容变更
4. 流数据损坏
性能优化[编辑 | 编辑源代码]
- 使用缓冲流(
BufferedInputStream
) - 批量处理对象
- 考虑替代序列化方案(如Protocol Buffers)
最佳实践[编辑 | 编辑源代码]
1. 始终显式声明serialVersionUID
2. 对敏感字段使用transient
3. 实现readObject()
时保持防御性编程
4. 在finally块中关闭流或使用try-with-resources