带有不可序列化部分的 Java 序列化

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/95181/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 08:14:49  来源:igfitidea点击:

Java Serialization with non serializable parts

javaserialization

提问by Burkhard

I have:

我有:

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

In MyClass2 is a property that is not serializable. How can I serialize (and de-serialize) this object?

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

Correction: MyClass2 is, of course, not an interface but a class.

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

采纳答案by Scott Bale

As someone else noted, chapter 11 of Josh Bloch's Effective Javais an indispensible resource on Java Serialization.

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

A couple points from that chapter pertinent to your question:

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

  • assuming you want to serialize the state of the non-serializable field in MyClass2, that field must be accessible to MyClass, either directly or through getters and setters. MyClass will have to implement custom serialization by providing readObject and writeObject methods.
  • the non-serializable field's Class must have an API to allow getting it's state (for writing to the object stream) and then instantiating a new instance with that state (when later reading from the object stream.)
  • per Item 74 of Effective Java, MyClass2 musthave a no-arg constructor accessible to MyClass, otherwise it is impossible for MyClass to extend MyClass2 and implement Serializable.
  • 假设您要序列化 ​​MyClass2 中不可序列化字段的状态,则 MyClass 必须可以直接或通过 getter 和 setter 访问该字段。MyClass 必须通过提供 readObject 和 writeObject 方法来实现自定义序列化。
  • 不可序列化字段的类必须有一个 API 以允许获取它的状态(用于写入对象流),然后使用该状态实例化一个新实例(稍后从对象流中读取时)。
  • 根据 Effective Java 的 Item 74,MyClass2必须有一个可供 MyClass 访问的无参数构造函数,否则 MyClass 不可能扩展 MyClass2 并实现 Serializable。

I've written a quick example below illustrating this.

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


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;
  }
}

回答by Mike Deck

MyClass2 is just an interface so techinicaly it has no properties, only methods. That being said if you have instance variables that are themselves not serializeable the only way I know of to get around it is to declare those fields transient.

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

ex:

前任:

private transient Foo foo;

When you declare a field transient it will be ignored during the serialization and deserialization process. Keep in mind that when you deserialize an object with a transient field that field's value will always be it's default (usually null.)

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

Note you can also override the readResolve() method of your class in order to initialize transient fields based on other system state.

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

回答by sk.

You will need to implement writeObject()and readObject()and do manual serialization/deserialization of those fields. See the javadoc page for java.io.Serializablefor details. Josh Bloch's Effective Javaalso has some good chapters on implementing robust and secure serialization.

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

回答by Hank

You can start by looking into the transientkeyword, which marks fields as not part of the persistent state of an object.

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

回答by ykaganovich

If you can modify MyClass2, the easiest way to address this is declare the property transient.

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

回答by Boris Terzic

XStreamis a great library for doing fast Java to XML serialization for any object no matter if it is Serializable or not. Even if the XML target format doesn't suit you, you can use the source code to learn how to do it.

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

回答by Steve Jessop

Depends why that member of MyClass2 isn't serializable.

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

If there's some good reason why MyClass2 can't be represented in a serialized form, then chances are good the same reason applies to MyClass, since it's a subclass.

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

It may be possible to write a custom serialized form for MyClass by implementing readObject and writeObject, in such a way that the state of the MyClass2 instance data in MyClass can be suitably recreated from the serialized data. This would be the way to go if MyClass2's API is fixed and you can't add Serializable.

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

But first you should figure out why MyClass2 isn't serializable, and maybe change it.

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

回答by Tom Hawtin - tackline

A useful approach for serialising instances of non-serializable classes (or at least subclasses of) is known a Serial Proxy. Essentially you implement writeReplace to return an instance of a completely different serializable class which implements readResolve to return a copy of the original object. I wrote an example of serialising java.awt.BasicStroke on Usenet

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

回答by Burkhard

Several possibilities poped out and i resume them here:

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

  • Implement writeObject() and readObject() as sksuggested
  • declare the property transient and it won't be serialized as first stated by hank
  • use XStream as stated by boris-terzic
  • use a Serial Proxy as stated by tom-hawtin-tackline
  • 按照sk 的建议实现 writeObject() 和 readObject()
  • 声明属性瞬态,它不会像hank最初声明的那样被序列化
  • 使用Boris-terzic所述的 XStream
  • 使用tom-hawtin-tackline所述的串行代理

回答by Radim Burget

If possible, the non-serialiable parts can be set as transient

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

private transient SomeClass myClz;

Otherwise you can use Kryo. Kryo is a fast and efficient object graph serialization framework for Java (e.g. JAVA serialization of java.awt.Color requires 170 bytes, Kryo only 4 bytes), which can serialize also non serializable objects. Kryo can also perform automatic deep and shallow copying/cloning. This is direct copying from object to object, not object->bytes->object.

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

Here is an example how to use kryo

这是如何使用 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();

Serialized objects can be also compressed by registering exact serializer:

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

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