Java 对象序列化踩坑经验
- Java
- 2019-05-16
- 115热度
- 0评论
导航
0 背景
有一个集群相关的 bug,在我名下挂了两三个月,今天终于解决掉了,居然跟对象序列化有关。问题简化之后的现象是:一个 Java 对象,存入 redis 后再取出来,丢失了 id 字段。
1 结论
对于需要序列化的类,一定要在继承层次的最顶层实现 Serializable 接口,否则父类的私有字段无法序列化。
2 实验研究
2.1 准备工作
我们创建两个类,一个是 Animal,另一个是 Dog。
源码如下:
// Animal.java
abstract class Animal {
private String id;
Animal() {
}
Animal(String id) {
this.id = id;
}
String getID() {
return id;
}
void setID(String id) {
this.id = id;
}
}
// Dog.java
class Dog extends Animal implements Serializable {
private String name;
Dog(String id, String name) {
super(id);
this.name = name;
}
String getName() {
return name;
}
void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Dog)) {
return false;
}
return getID().equals(((Dog)obj).getID())
&& getName().equals(((Dog)obj).getName());
}
@Override
public String toString() {
return "Dog[" + getID() + "]: " + getName();
}
}
2.2 复现问题
编写如下单元测试,运行,发现有报错。基本还原了我遇到的 bug。
public class DogTest {
private Dog dog;
@Before
public void setUp() {
dog = new Dog("dog-001", "WangCai");
}
@Test
public void testSerializable() throws IOException, ClassNotFoundException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(baos);
os.writeObject(dog);
os.close();
ObjectInputStream is = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
Dog dog2 = (Dog)is.readObject();
is.close();
assertEquals(dog, dog2); // 报错
}
}
截图为证
2.3 修复问题
让 Animal 去实现 Serializable 接口(而不是让 Dog 去实现)。
修改后的代码如下(省略号代表未改动的代码):
// Animal.java
abstract class Animal implements Serializable {
...
// Dog.java
class Dog extends Animal {
...
再跑一遍单元测试,发现没有问题了。