Java:Socket二次写入后HashMap值未更新问题排查
嘿,这个问题我太熟悉了——这是Java对象序列化的默认缓存机制在搞鬼!
当你用ObjectOutputStream.writeObject()写入同一个对象引用时,第一次序列化会把完整的对象数据写入流,同时把这个对象的引用缓存起来。后续再写同一个引用时,它只会发送一个“引用标记”,根本不会重新序列化对象的最新状态!
看你的客户端代码,循环里一直复用同一个updateCO对象,哪怕你修改了它内部的changes HashMap,ObjectOutputStream也不会重新序列化这个对象的最新状态——所以服务端拿到的永远是第一次序列化时的旧对象,自然看不到Id的更新。
1. 写入前重置流(最快捷)
在每次调用writeObject()之前,加一行objectOutputStream.reset(),它会清空流的对象缓存,强制重新序列化当前对象的最新状态:
while(true){ i++; changes.put("Id",""+i); objectOutputStream.reset(); // 关键新增代码 objectOutputStream.writeObject(updateCO); objectInputStream.readObject(); changes.put("username","3"); System.out.println(changes.get("Id")); if(i>50) break; }
2. 每次循环新建对象(最稳妥,无缓存问题)
如果场景允许,每次循环都创建新的UpdateContextObject实例,这样每次写入的都是全新的对象引用,流会完整序列化最新内容:
while(true){ i++; UpdateContextObject updateCO = new UpdateContextObject(); updateCO.setTable("users"); updateCO.setAttributes(new HashMap<>()); Map<String,String> changes = new HashMap<>(); changes.put("Id",""+i); changes.put("username","3"); updateCO.setChanges(changes); objectOutputStream.writeObject(updateCO); objectInputStream.readObject(); System.out.println(changes.get("Id")); if(i>50) break; }
3. 使用writeUnshared()替代writeObject()
这个方法会绕过对象缓存,每次都序列化当前对象的状态(注意:如果对象内部还有共享的嵌套对象,可能需要额外处理嵌套对象的序列化):
objectOutputStream.writeUnshared(updateCO);
4. 自定义序列化逻辑(进阶)
让UpdateContextObject实现Externalizable接口,自己编写writeExternal()和readExternal()方法,手动控制序列化过程,确保每次都写入最新的HashMap数据。这个方法适合需要高度自定义序列化的场景,相对复杂一些。
先试试第一种方法,改完就能看到服务端日志开始打印递增的Id值啦!
内容的提问来源于stack exchange,提问作者user2962142

