writeunshared的作用是绕过objectoutputstream的对象引用缓存机制,每次调用都完整序列化对象且不分配handle;它不改变对象本身,仅使序列化器不记录该对象、不复用其句柄。

writeUnshared 的作用不是“强行写入新对象”,而是**绕过 ObjectOutputStream 的内置对象引用缓存机制,对同一对象多次调用时每次都序列化其完整数据(而非写入引用标记)**。它不改变对象本身,也不“避免引用复用”——它只是让序列化器不记录该对象、不后续复用其句柄。
理解 writeUnshared 的真实行为
ObjectOutputStream 内部维护一个“已写对象表”(handle table),默认情况下:
- 第一次写入某个对象(如 obj)时,序列化其内容,并分配一个整数 handle(如 0x00000001);
- 第二次再写入 同一个对象实例(
obj == obj)时,不重复序列化,只写入一个 TC_REFERENCE + handle(即“复用引用”); -
writeUnshared(obj) 则跳过 handle 表登记:每次调用都完整序列化 obj 的字段,且不为其分配 handle,后续即使再用
writeUnshared或writeObject写它,也仍会重新序列化(或复用,取决于调用方式)。
什么时候该用 writeUnshared?
典型场景是:你需要**语义上“独立副本”效果,且接收方反序列化后得到的是不同实例(即使内容相同)**,例如:
- 发送配置快照,每次都是逻辑上独立的瞬时状态(如监控指标、事件 payload),不希望反序列化后多个引用指向同一对象;
- 对象实现了自定义
readResolve或含单例逻辑,但你明确要绕过它,强制走常规反序列化流程; - 调试时想确认某对象是否被真正复制(比如验证 deep copy 是否生效),临时用
writeUnshared观察字节流差异。
它不能替代深拷贝或解决共享状态问题
注意:writeUnshared 不等于深拷贝,也不影响对象内部字段的引用关系:
- 若 obj 包含字段
String name和List<item> items</item>,writeUnshared(obj)仍会按需复用name(字符串常量池)、复用items中已出现过的Item实例(如果之前写过); - 它只作用于被传入的顶层对象本身,不递归“取消”其子对象的引用缓存;
- 若想真正隔离状态,必须在序列化前手动克隆对象(如实现
Cloneable+clone(),或用序列化+反序列化做深拷贝)。
正确使用示例
以下代码确保两次写入 config 得到两个独立实例(反序列化后 == 为 false):
Config config = new Config("db", 8080);
out.writeUnshared(config); // 完整序列化,不登记 handle
out.writeUnshared(config); // 再次完整序列化,不查表、不复用
// 反序列化端:readUnshared() 两次,得到两个不同 Config 实例
⚠️ 关键点:反序列化端也必须用 ObjectInputStream.readUnshared(),否则仍可能因缓存返回同一实例(尤其在连续读取时)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











