Effective Java表示 readResolve仅在所有字段都是暂时的 时才有效。 这不是一个错误吗?为什么Java创建者会做这样的事情?
- 更新
抱歉,我的意思是More Effective Java,请参阅幻灯片30.
答案 0 :(得分:2)
为完整起见,幻灯片29为:
项目77:流行测验:此类是单身吗?
public class Elvis implements Serializable { public static final Elvis INSTANCE = new Elvis(); private Elvis() { } private final String[] favoriteSongs = { "Hound Dog", "Heartbreak Hotel" }; public void printFavorites() { System.out.println(Arrays.toString(favoriteSongs)); } private Object readResolve() { return INSTANCE; } }
幻灯片30是:
答案:不幸的是
第一版超越了readResolve的力量
>猫王有一个非传染性的领域 (最爱的歌曲)
>巧妙 精心设计的攻击可以保存参考 反序列化
猫王实例的时候 此字段已反序列化
- 详情请见ElvisStealer(第77项)
> readResolve仅适用于 所有字段都是短暂的
第77项是:
项目77:对于实例控制,首选枚举类型为readResolve
...
如果是Elvis课程 实现Serializable,如下 readResolve方法就足够了 保证单身人士的财产:
// readResolve for instance control - you can do better! private Object readResolve() { // Return the one true Elvis and let the garbage collector // take care of the Elvis impersonator. return INSTANCE; }
此方法忽略反序列化 对象,返回尊贵 Elvis实例是在创建时创建的 该课程已初始化。因此, 猫王的序列化形式 实例不需要包含任何真实的 数据;所有实例字段都应该是 宣布短暂。事实上,如果你 例如,依赖于readResolve 控制,所有实例字段 对象引用类型必须是 宣称是暂时的。否则就是 确定的攻击者可能会这样做 保护对反序列化的引用 对象在readResolve方法之前 运行,使用的技术 与MutablePeriod模糊地相似 项目76中的攻击。</ p>
攻击有点复杂,但是 根本的想法很简单。如果一个 单身包含一个非瞬态 对象引用字段,内容 该字段将被反序列化 在单身人士的readResolve之前 方法运行。这样就可以了 制作流“偷”参考 原来反序列化的 当时单身人士的内容 对象引用字段是 反序列化。
如果所有字段都是瞬态的,那么这种情况不仅仅是有效的,如果所有对象引用都是瞬态的,那么它只是安全。您引用的幻灯片更进一步说是任何字段都是非瞬态的,攻击者可以获取对反序列化对象的引用。
至于为什么:人们可以争辩说Josh Bloch使用readResolve()
作为单身人士是无意中使用了API,创作者在创作时并没有想到这一点。你也可以认为这只是一种不可预见的后果。但我不认为这是故意的弱点。
答案 1 :(得分:1)
我觉得你误解了Josh Bloch。来自java spec:
对于Serializable和Externalizable 类,readResolve方法允许 替换/解析对象的类 在流之前从流中读取 返回给来电者。通过 实现readResolve方法,a class可以直接控制类型 和它自己的实例的实例 被反序列化。
如你所见,这与你的陈述没什么相似。
我知道readResolve最典型的用例之一就是当你读取序列化的单例(或者你不想创建新实例的任何对象时,比如手动实现的枚举,即没有enum
关键字)。在readResolve中,您可以读取字段(如果它们存在)并决定应该返回哪些已创建的对象(通常是单例)而不是创建新实例。
答案 2 :(得分:0)
使用readResolve()时,目标JVM中的Singleton行为无论如何都会被违反,因为readResolve()将返回对JVM中已存在的INSTANCE的引用。
但是在调用readResolve()之前将调用readObject(),并且在反序列化期间将使用JVM的反射创建Singleton类的另一个对象。 无论如何,这个对象可能会被垃圾收集,但是暂时目标JVM有两个相同类型的实例可以击败“单身”。