什么时候使用 ObjectInputStream.readUnshared() 和 .readObject() ?

E.S*_*.S. 6 java oracle objectoutputstream objectinputstream java-7

Java ObjectInputStream中有两个类似的方法:

读取未共享()

读取对象()

文档指出:

public Object readUnshared() throws IOException, ClassNotFoundException

从 ObjectInputStream 读取“非共享”对象。此方法与 readObject 相同,只不过它阻止对 readObject 和 readUnshared 的后续调用返回对通过此调用获得的反序列化实例的其他引用。具体来说:

如果调用 readUnshared 来反序列化反向引用(之前已写入流的对象的流表示形式),则会抛出 ObjectStreamException。

如果 readUnshared 成功返回,则任何后续尝试反序列化对 readUnshared 反序列化的流句柄的反向引用都将导致抛出 ObjectStreamException。

通过 readUnshared 反序列化对象会使与返回的对象关联的流句柄无效。请注意,这本身并不总是保证 readUnshared 返回的引用是唯一的;反序列化对象可以定义一个 readResolve 方法,该方法返回对其他方可见的对象,或者 readUnshared 可以返回可在流中的其他位置或通过外部方式获取的 Class 对象或枚举常量。如果反序列化对象定义了 readResolve 方法并且调用该方法返回一个数组,则 readUnshared 返回该数组的浅表克隆;这保证了返回的数组对象是唯一的,并且无法通过对 ObjectInputStream 调用 readObject 或 readUnshared 再次获取,即使底层数据流已被操作。

重写此方法的 ObjectInputStream 子类只能在拥有“enableSubclassImplementation”SerializedPermission 的安全上下文中构造;任何在没有此权限的情况下实例化此类子类的尝试都将导致抛出 SecurityException。

但我想知道是否有人在现实生活中对此有.readUnshared()用途.readObject()

Fox*_*BOA 2

我相信这可能是与安全相关的非常特殊的情况(?)。就像这个(来自这里)。

A.6 保护非共享的反序列化对象

如果类具有任何私有或包私有对象引用字段,并且该类依赖于这些对象引用在类(或包)外部不可用的事实,则必须在反序列化过程中防御性地复制引用的对象,否则应使用 ObjectOutputStream.writeUnshared 和 ObjectInputStream.readUnshared 方法(在 JavaTM 2 SDK 标准版 1.4 版中引入)来确保对内部对象的唯一引用。

在复制方法中,从流中反序列化的子对象应被视为“不受信任的输入”:新创建的对象,初始化为与反序列化的子对象具有相同的值,应由 readObject 替换子对象方法。例如,假设一个对象有一个私有字节数组字段 b,该字段必须保持私有:

 private void readObject(ObjectInputStream s)
    throws IOException, ClassNotFoundException
{
    s.defaultReadObject();

    b = (byte[])b.clone();

    if (<invariants are not satisfied>)
        throw new java.io.StreamCorruptedException();
}
Run Code Online (Sandbox Code Playgroud)

当考虑包含对可变子对象的内部(必须是私有)引用的不可变对象的序列化时,这个问题尤其重要。如果在容器对象的反序列化期间没有采取特殊措施来复制子对象,则对序列化流具有写访问权限的恶意方可能会通过伪造对其可变子对象的引用并使用这些引用来破坏容器对象的不变性改变容器对象的内部状态。因此,在这种情况下,不可变容器类必须提供特定于类的反序列化方法,该方法为其反序列化的每个可变组件对象创建私有副本。请注意,为了保持不变性,没有必要复制不可变的组件对象。

同样重要的是要注意,调用克隆可能并不总是防御性复制子对象的正确方法。如果不能指望克隆方法产生独立的副本(并且不“窃取”对副本的引用),则应使用替代方法来产生副本。如果子对象的类不是最终的,则应始终使用替代的复制方法,因为它调用的克隆方法或辅助方法可能会被子类覆盖。

从 JavaTM 2 SDK 标准版 1.4 版开始,还可以使用 ObjectOutputStream.writeUnshared 和 ObjectInputStream.readUnshared 方法确保对反序列化对象的唯一引用,从而避免防御性复制的复杂性、性能成本和内存开销。