Java依赖对象序列化一致性问题:反序列化后Address引用ID不同
你遇到的断言失败问题,核心原因不是序列化逻辑本身的问题,而是默认的equals()方法行为和你对“相等”的预期不匹配,再加上两个类的serialVersionUID设置错误,我们一步步来解决:
1. 为什么对象ID变化是正常的?
反序列化的本质是在内存中创建一个全新的对象实例,把序列化时保存的属性值填充进去,所以反序列化后的对象内存地址(就是你看到的Address@6b143ee9这种)必然和原对象不同——这是正常现象,不用纠结这个ID的变化,真正要关注的是对象的属性值是否一致,而这需要通过正确的equals()方法来判断。
2. 断言失败的核心原因:未重写equals()和hashCode()
Java默认的Object.equals()方法是比较对象的引用地址,反序列化出来的是新对象,引用地址和原对象不一样,所以Assert.assertEquals(employee, deserialized1)必然失败。你需要为Employee和Address类重写equals()和hashCode()方法,让它们基于属性值来判断相等性。
3. 额外问题:错误的serialVersionUID设置
你给Employee和Address设置了完全相同的serialVersionUID,这是不正确的。每个可序列化类都应该有自己唯一的serialVersionUID,这个值用来验证序列化和反序列化时类结构的一致性,如果后续修改类结构(比如新增字段),只要UID不变,反序列化仍能兼容(前提是符合序列化规则)。
小笔误修正
你的测试代码里有个小问题:employee.setAddress(addressList)应该是employee.setAddresses(addressList),因为Employee类的字段是private List<Address> addresses,确保setter方法名和字段对应,否则序列化的对象属性会是空,也可能导致断言失败。
修改后的代码示例
重写Address类的equals()和hashCode()
import java.io.Serializable; import java.util.Objects; public class Address implements Serializable { // 改为类专属的唯一serialVersionUID private static final long serialVersionUID = 123456789L; private String pincode; private String area; private String state; // 构造器、getter、setter方法省略 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Address address = (Address) o; return Objects.equals(pincode, address.pincode) && Objects.equals(area, address.area) && Objects.equals(state, address.state); } @Override public int hashCode() { return Objects.hash(pincode, area, state); } }
重写Employee类的equals()和hashCode()
import java.io.Serializable; import java.util.List; import java.util.Objects; public class Employee implements Serializable { // 改为类专属的唯一serialVersionUID private static final long serialVersionUID = 987654321L; private Long id; private List<Address> addresses; private String name; // 构造器、getter、setter方法省略 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Employee employee = (Employee) o; return Objects.equals(id, employee.id) && Objects.equals(addresses, employee.addresses) && Objects.equals(name, employee.name); } @Override public int hashCode() { return Objects.hash(id, addresses, name); } }
确保序列化/反序列化方法正确
你的测试中用到的serialize和deserialize方法,要确保是用标准的ObjectOutputStream和ObjectInputStream实现的,比如:
private byte[] serialize(Object obj) throws IOException { ByteArrayOutputStream bos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(bos)) { oos.writeObject(obj); } return bos.toByteArray(); } private Object deserialize(byte[] data) throws IOException, ClassNotFoundException { ByteArrayInputStream bis = new ByteArrayInputStream(data); try (ObjectInputStream ois = new ObjectInputStream(bis)) { return ois.readObject(); } }
验证修改后的效果
做完以上修改后,再运行testConsistency测试,断言就会通过——因为现在equals()方法会比较两个对象的属性值是否一致,而不是比较引用地址。
内容的提问来源于stack exchange,提问作者Gaurav Sinha

