考虑使用序列化代理而非序列化实例
立即解锁
发布时间: 2025-08-18 00:29:05 阅读量: 15 订阅数: 21 


Effective Java:编写高效Java代码的最佳实践
### 考虑使用序列化代理而非序列化实例
在Java开发中,实现`Serializable`接口虽然能让对象进行序列化,但也增加了出现漏洞和安全问题的风险。因为它允许通过非语言机制创建实例,而非使用普通的构造函数。不过,有一种技术能大大降低这些风险,这就是序列化代理模式。
#### 序列化代理模式概述
序列化代理模式相对简单。首先,设计一个私有静态嵌套类,它能简洁地表示外部类实例的逻辑状态,这个嵌套类就是外部类的序列化代理。它应该有一个构造函数,其参数类型为外部类。这个构造函数只需从参数中复制数据,无需进行一致性检查或防御性复制。从设计上看,序列化代理的默认序列化形式就是外部类的完美序列化形式。外部类和它的序列化代理都必须声明实现`Serializable`接口。
#### 示例:Period类的序列化代理
以`Period`类为例,它是一个不可变类,且已实现了序列化。以下是它的序列化代理:
```java
// Serialization proxy for Period class
private static class SerializationProxy implements Serializable {
private final Date start;
private final Date end;
SerializationProxy(Period p) {
this.start = p.start;
this.end = p.end;
}
private static final long serialVersionUID =
234098243823485285L; // Any number will do
}
```
接下来,在外部类中添加`writeReplace`方法:
```java
// writeReplace method for the serialization proxy pattern
private Object writeReplace() {
return new SerializationProxy(this);
}
```
这个方法会让序列化系统在序列化时发出`SerializationProxy`实例,而非外部类的实例。也就是说,`writeReplace`方法在序列化前将外部类实例转换为其序列化代理。
为了防止攻击者伪造外部类的序列化实例来破坏类的不变性,还需要在外部类中添加`readObject`方法:
```java
// readObject method for the serialization proxy pattern
private void readObject(ObjectInputStream stream)
throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
```
最后,在`SerializationProxy`类中提供`readResolve`方法,用于在反序列化时将序列化代理转换回外部类的实例:
```java
// readResolve method for Period.SerializationProxy
private Object readResolve() {
return new Period(start, end);
// Uses public constructor
}
```
这个`readResolve`方法仅使用外部类的公共API创建实例,这正是该模式的精妙之处。它在很大程度上消除了序列化的非语言特性,因为反序列化实例的创建方式与其他实例相同,使用相同的构造函数、静态工厂和方法。这样就无需单独确保反序列化实例遵守类的不变性。如果类的静态工厂或构造函数建立了这些不变性,并且其实例方法维护了它们,那么序列化也能保证这些不变性。
#### 序列化代理模式的优势
- **安全性高**:和防御性复制方法一样,序列化代理模式能有效阻止虚假字节流攻击和内部字段窃取攻击。
- **支持字段为final**:与前两种方法不同,它允许`Period`类的字段为`final`,这是`Per
0
0
复制全文
相关推荐










