【问题标题】:Java Serialization with non serializable parts具有不可序列化部分的 Java 序列化
【发布时间】:2008-09-18 18:15:15
【问题描述】:

我有:

class MyClass extends MyClass2 implements Serializable {
  //...
}

在 MyClass2 中是不可序列化的属性。如何序列化(和反序列化)这个对象?

更正:MyClass2 当然不是接口而是类。

【问题讨论】:

  • 能否修改MyClass2?不可序列化的属性值是否需要通过序列化来保留?正确的方法取决于此。
  • MyClass2 可以修改。但是,最好不要这样做,因为我建立在它之上,并且可以被其他人修改。

标签: java serialization


【解决方案1】:

正如其他人所指出的,Josh Bloch 的Effective Java 第 11 章是 Java 序列化不可或缺的资源。

该章节中与您的问题相关的几点:

  • 假设您想要序列化 ​​MyClass2 中不可序列化字段的状态,MyClass 必须可以直接或通过 getter 和 setter 访问该字段。 MyClass 必须通过提供 readObject 和 writeObject 方法来实现自定义序列化。
  • 不可序列化字段的 Class 必须有一个 API 以允许获取其状态(用于写入对象流),然后使用该状态实例化一个新实例(稍后从对象流中读取时)。
  • 根据 Effective Java 的第 74 条,MyClass2 必须有一个 MyClass 可访问的无参数构造函数,否则 MyClass 不可能扩展 MyClass2 并实现 Serializable。

我在下面写了一个简单的例子来说明这一点。


class MyClass extends MyClass2 implements Serializable{

  public MyClass(int quantity) {
    setNonSerializableProperty(new NonSerializableClass(quantity));
  }

  private void writeObject(java.io.ObjectOutputStream out)
  throws IOException{
    // note, here we don't need out.defaultWriteObject(); because
    // MyClass has no other state to serialize
    out.writeInt(super.getNonSerializableProperty().getQuantity());
  }

  private void readObject(java.io.ObjectInputStream in)
  throws IOException {
    // note, here we don't need in.defaultReadObject();
    // because MyClass has no other state to deserialize
    super.setNonSerializableProperty(new NonSerializableClass(in.readInt()));
  }
}

/* this class must have no-arg constructor accessible to MyClass */
class MyClass2 {

  /* this property must be gettable/settable by MyClass.  It cannot be final, therefore. */
  private NonSerializableClass nonSerializableProperty;

  public void setNonSerializableProperty(NonSerializableClass nonSerializableProperty) {
    this.nonSerializableProperty = nonSerializableProperty;
  }

  public NonSerializableClass getNonSerializableProperty() {
    return nonSerializableProperty;
  }
}

class NonSerializableClass{

  private final int quantity;

  public NonSerializableClass(int quantity){
    this.quantity = quantity;
  }

  public int getQuantity() {
    return quantity;
  }
}

【讨论】:

  • Aww 我希望有像 @NonSerialization 这样的注释:-/
【解决方案2】:

MyClass2 只是一个接口,所以技术上它没有属性,只有方法。话虽如此,如果您有本身不可序列化的实例变量,我知道解决它的唯一方法是声明这些字段是瞬态的。

例如:

private transient Foo foo;

当您声明一个字段瞬态时,它将在序列化和反序列化过程中被忽略。请记住,当您反序列化具有瞬态字段的对象时,该字段的值将始终为默认值(通常为 null。)

请注意,您还可以覆盖类的 readResolve() 方法,以便根据其他系统状态初始化瞬态字段。

【讨论】:

    【解决方案3】:

    如果可能,可以将不可串行部分设置为瞬态

    private transient SomeClass myClz;
    

    否则你可以使用Kryo。 Kryo 是一个快速高效的 Java 对象图序列化框架(例如 java.awt.Color 的 JAVA 序列化需要 170 个字节,Kryo 只需 4 个字节),它也可以序列化不可序列化的对象。 Kryo 还可以执行自动深浅复制/克隆。这是从对象直接复制到对象,而不是object->bytes->object

    这里是一个如何使用kryo的例子

    Kryo kryo = new Kryo();
    // #### Store to disk...
    Output output = new Output(new FileOutputStream("file.bin"));
    SomeClass someObject = ...
    kryo.writeObject(output, someObject);
    output.close();
    // ### Restore from disk...
    Input input = new Input(new FileInputStream("file.bin"));
    SomeClass someObject = kryo.readObject(input, SomeClass.class);
    input.close();
    

    序列化的对象也可以通过注册精确的序列化器来压缩:

    kryo.register(SomeObject.class, new DeflateCompressor(new FieldSerializer(kryo, SomeObject.class)));
    

    【讨论】:

      【解决方案4】:

      如果您可以修改 MyClass2,解决此问题的最简单方法是声明属性瞬态。

      【讨论】:

      • 这不能回答问题 - 问题指出 MyClass2 包含不可序列化的属性,理想情况下不应修改 MyClass2。
      • 你说得对,我回答的时候没有仔细阅读这个问题。我修改了答案。
      【解决方案5】:

      取决于为什么 MyClass2 的该成员不可序列化。

      如果 MyClass2 不能以序列化形式表示是有充分理由的,那么很有可能同样的原因也适用于 MyClass,因为它是一个子类。

      可以通过实现 readObject 和 writeObject 为 MyClass 编写自定义序列化表单,这样可以从序列化数据中适当地重新创建 MyClass 中 MyClass2 实例数据的状态。如果 MyClass2 的 API 是固定的并且您无法添加 Serializable,这将是可行的方法。

      但首先你应该弄清楚为什么 MyClass2 不可序列化,并可能改变它。

      【讨论】:

        【解决方案6】:

        您需要实现writeObject()readObject() 并对这些字段进行手动序列化/反序列化。有关详细信息,请参阅java.io.Serializable 的 javadoc 页面。 Josh Bloch 的 Effective Java 也有一些关于实现健壮和安全的序列化的好章节。

        【讨论】:

          【解决方案7】:

          您可以从查看 transient 关键字开始,它将字段标记为不属于对象的持久状态。

          【讨论】:

            【解决方案8】:

            出现了几种可能性,我在这里恢复它们:

            • 按照sk 的建议实现 writeObject() 和 readObject()
            • 声明属性transient,它不会像hank最初所说的那样被序列化
            • 使用boris-terzic 所述的XStream
            • 使用tom-hawtin-tackline 所述的串行代理

            【讨论】:

              【解决方案9】:

              XStream 是一个很棒的库,可以对任何对象进行快速的 Java 到 XML 序列化,无论它是否可序列化。即使 XML 目标格式不适合您,您也可以使用源代码来学习如何操作。

              【讨论】:

              • 嗨,您能解释一下如何将 XStream 与不可序列化的对象一起使用吗?
              • 文档非常不言自明,教程从不实现 Serializable 的类开始,向您展示如何序列化它们:x-stream.github.io/tutorial.html
              【解决方案10】:

              序列化不可序列化类(或至少其子类)实例的有用方法称为串行代理。本质上,您实现 writeReplace 以返回完全不同的可序列化类的实例,该类实现 readResolve 以返回原始对象的副本。我在Usenet上写了一个序列化java.awt.BasicStroke的例子

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-05-07
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多