java - 添加新成员变量后反序列化旧对象:Java
问题描述
我有一个名为 Employee 的类,结构如下:
package Serializable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class Employee implements Serializable {
private static final long serialVersionUID = -299482035708790407L;
private String name;
private int age;
public String getName() {
return name;
}
public int getAge() {
return age;
}
void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Name: " + name + ", " + "age: " + age ;
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
setName(ois.readUTF());
setAge(ois.readInt());
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeUTF(name);
out.writeInt(age);
}
}
我编写了 WriteEmployee.java,它将 Employee 对象写入文件'employee.ser',如下所示:
public class WriteEmployee {
public static void main(String[] args) {
FileOutputStream fos = null;
ObjectOutputStream os = null;
try {
fos = new FileOutputStream("employee.ser");
os = new ObjectOutputStream(fos);
Employee e = new Employee();
e.setName("User123");
e.setAge(28);
((ObjectOutputStream) os).writeObject(e);
} catch (IOException e) {
System.out.println(e.getMessage());
} finally {
if (os !=null ) {
try {
os.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
}
}
在 Employee.java 中,我决定添加“性别”字段,所以我将此代码添加到 Employee.java
private String gender;
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
我还修改了 Employee.java 中的 readObject() 和 writeObject() 如下:
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
setName(ois.readUTF());
setAge(ois.readInt());
setGender(ois.readUTF());
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeUTF(name);
out.writeInt(age);
out.writeUTF(gender);
}
我正在尝试阅读我的“employee.ser”文件,该文件仅包含有关姓名和年龄的数据,但不包含有关性别的数据。但是,我得到'null'作为回报。这是我的 ReadEmployee.java 代码:
public class ReadEmployee {
public static void main(String[] args) {
FileInputStream fis = null;
ObjectInputStream is = null;
try {
fis = new FileInputStream("employee.ser");
is = new ObjectInputStream(fis);
Employee e = (Employee)is.readObject();
System.out.println(e);
fis.close();
is.close();
} catch (IOException | ClassNotFoundException e) {
System.out.println(e.getMessage());
}
}
}
我遵循了我在这个论坛上找到的建议,其他在线教程是
- 添加一个serialVersionUID
- 编写自定义的 readObject() 和 writeObject() 方法
我找不到任何教程,他们可以展示编写旧对象、添加新字段和适当读取旧对象的示例。任何人都可以通过示例代码指导我需要更改的内容,以便我可以在通过添加新成员变量修改我的类后读取旧对象,保持我的 UID 不变。
解决方案
您的新 writeObject 方法正在写入与先前序列化表单不兼容的数据。有一些正确的方法可以“进化”一个可序列化的类,但完全替换它的序列化形式并不是其中之一。
只要您的 writeObject 方法自己写入字段,您的 readObject 方法就无法检测到 ObjectInputStream 中的序列化形式(旧的还是新的)。
您根本不需要覆盖 readObject 和 writeObject。完全省略它们。反序列化未写入gender
字段的旧版本只会导致 Employee 对象带有 null gender
。
如果您坚持设置gender
为非空值,则可以覆盖 readObject(但不要覆盖 writeObject)。在执行任何其他操作之前,您必须调用defaultReadObject方法;这将读取所有旧字段的值。然后,您可以决定如何处理 null 性别:
private void readObject(ObjectInputStream ois)
throws ClassNotFoundException,
IOException {
ois.defaultReadObject();
if (gender == null) {
gender = "unknown";
}
}
您应该在 Employee 的原始版本中包含一个 serialVersionUID:
private static final long serialVersionUID = 1;
但你没有,这意味着 Java 从 Employee 字段的散列中计算了一个。添加该gender
字段后,将创建一个新的且不兼容的默认 serialVersonUID,因为还有一个要散列的字段,这将阻止反序列化。
您可以通过告诉 Java 将 Employee 的新版本与旧版本相同来解决此问题,以达到反序列化匹配的目的,通过计算旧版本的默认 serialVersionUID 并让新版本显式使用它:
- 注释掉该
gender
字段及其访问器方法。 - 使用每个 JDK 附带的serialver工具来确定为 Employee 类自动计算的默认 serialVersionUID。
- 恢复
gender
字段和关联的访问器方法。 - 将新的 serialVersionUID 复制到类中:(
private static final long serialVersionUID = 123456789abcdefL;
替换123456789abcdefL
为 serialver 命令的输出。)
推荐阅读
- google-cloud-platform - 如何在生产中更改 GCP 发布/订阅过滤器?
- react-native - React Native i18n 不能与警报一起使用?
- python - 如何解决 django 上的 post 请求错误
- sql - 尝试在 SQL 中透视表时出现未知语法错误
- php - 如何在laravel中找到我的表中剩余字段的值大于零的行
- recursion - TBS小而强:如何递归填充数据
- android - 如何使用recyclerview设置动画drawable?
- c++ - 矩形矩阵的 SIMD 矩阵乘法
- zsh - 我的 .zshrc 抛出 VCS_INFO_set:12: bad math expression: invalid character: {
- c# - 在 C# 中,如何处理具有多个线程/任务但有条件的大型文本文件?