java - 反序列化时如何正确使用 transient 关键字?

标签 java design-patterns serializable transient

我有以下类(class)

public class Num implements Serializable, Observable {
    private int num; // number which should be serialized

    // listeners who are watching for changes in this Num
    // No need to serialize these -- pointless to do so.
    private transient Set<Observer> listeners = new HashSet<>();

    @Override public void addListener(Observer o) { listeners.add(o); }

    @Override public void removeListener(Observer o) { listeners.remove(o); }

    public void set(int newVal) {
        if (num != newVal) {
            num = newVal; // set the value.
            for (Observer o : listeners)
                o.notify(); // Notify listeners that the value changed.
        }
    }
}

当我序列化这个类时,它运行良好并且 num 被保存了。当我反序列化该类时,num 被加载,但 listeners 未被加载并被设置为 null。然后我的程序在 for (Observer o : listeners) 行崩溃。

我想出了一些解决方案,但它们都是糟糕的解决方案。

1) 有一个“重建”transient 字段的设置方法。

public void setup() {
    if (listeners != null) throw new Exception("Already setup!");
    listeners = new HashSet<>();
}

这种方式很烦人,因为反序列化方法需要记住设置对象。对于从事该项目的其他人来说,这是非常不直观的。

2)set方法自动检查和修复自身

public void set(int newVal) {
    if (num != newVal) {
        if (listeners == null) listeners = new HashSet<>();
        ...
    }
}

这种方式也很糟糕,因为检查会一遍又一遍地发生,即使它只需要完成一次。

3)class Num 中移除 Observable,去掉 listeners 等。然后做一个非- 包含 Num 实例的可序列化类。

public class Num implements Serializable {
    public int num;
}

public class ObservableNum impements Observable {
    private Num n;

    public ObservableNum() { n = new Num(); } // constructor
    private ObservableNum(Num n) { this.n = n; } // constructor

    ...

    public static ObservableNum loadNum(...) {

        ObjectInputStream ois = ...;
        return new ObservableNum((Num) ois.readObject());
    }
}

但这种方式似乎也不必要地复杂。当然必须有更好的解决方案?如何正确使用 transient ?

最佳答案

来自 documentation :

There is, however, a strange yet crafty solution. By using a built-in feature of the serialization mechanism, developers can enhance the normal process by providing two methods inside their class files.

这些方法是:

  1. private void writeObject(ObjectOutputStream out) 抛出 IOException;
  2. private void readObject(ObjectInputStream in) 抛出 IOException, ClassNotFoundException;

因此,您只需要实现如下所示的 readObject,在默认反序列化后只需创建 HashSet 的新实例。示例:

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    this.listeners = new HashSet<>();
}

关于java - 反序列化时如何正确使用 transient 关键字?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58617322/

相关文章:

java - Java编译中错误的意外顺序

java - 数组的标准正态分布

java - Spring-Roo JPA 创建的实体未更新

c# - 双例模式实现

java - 禁用全窗口拖动

design-patterns - 数据绑定(bind)只用于 UI 吗?

c++ - 像交互式 shell 一样设计 python

java - HttpServlet为什么要实现Serializable?

c# - 为什么我不能在同一个类上使用 WCF DataContract 和 ISerializable?

gwt - 在客户端使用来自 GWT 中另一个 Maven 模块的 java 对象