4

动机:

为了帮助远程调试 (Java),能够请求远程服务器将任意对象发送到我的本地机器进行检查是很有用的。但是,这意味着远程服务器必须能够序列化一个在运行时事先不知道的任意 java 对象。

所以我四处打听,偶然发现了Kryo 序列化库。从Kryo 的文档中,一个主要特性是它在序列化任意 java 对象方面非常健壮。对象不必实现Serializable,不需要无参数构造函数即可反序列化,我什至不需要在序列化之前了解对象的结构。完美的!

问题:

所以为了测试 Kryo,我试着看看我是否可以序列化然后反序列化一个PrintWriter对象(即任意对象):

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import java.io.*;

public class SerializationTest {

    private static final String ioFileName = "someIO.bin";

    public static void main(String[] args) {

        // Create a PrintWriter object that I will later attempt to serialize
        PrintWriter outObj = null;
        try {
            outObj = new PrintWriter("textfile.txt");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        // Change the PrintWriter's state as a test for later to see if state is restored after serialization and deserialization
        outObj.println("Hello");   // "Hello" held in PrintWriter's buffer

        Kryo kryo = new Kryo();    // Initialize Kryo serialization
        writeObj(kryo, outObj);    // Save PrintWriter object to file with "Hello" still in its buffer

        // Read the previously saved Printwriter object (still with "Hello" in its buffer)
        PrintWriter inObj = (PrintWriter) readObj(kryo);

        inObj.close();    // commit "Hello" to disk (using deserialized object)
        outObj.close();   // commit "Hello" to disk (using original object)

        System.out.println(inObj);
    }

    public static Object readObj(Kryo kryo) {
        Object obj = null;
        try {
            Input input = new Input(new FileInputStream(ioFileName));
            obj = kryo.readClassAndObject(input);   // ERROR HERE!!
            input.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return obj;
    }

    public static void writeObj(Kryo kryo, Object obj) {
        try {
            Output output = new Output(new FileOutputStream(ioFileName));
            kryo.writeClassAndObject(output, obj);
            output.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

序列化工作正常,但在反序列化时,对第 39 行的调用kryo.readClassAndObject(input)给出以下内容IllegalAccessError

Exception in thread "main" java.lang.IllegalAccessError: tried to access class sun.nio.cs.UTF_8 from class sun.nio.cs.UTF_8ConstructorAccess
    at sun.nio.cs.UTF_8ConstructorAccess.newInstance(Unknown Source)
    at com.esotericsoftware.kryo.Kryo$DefaultInstantiatorStrategy$1.newInstance(Kryo.java:1234)
    at com.esotericsoftware.kryo.Kryo.newInstance(Kryo.java:1086)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.create(FieldSerializer.java:547)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:523)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:704)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:106)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:528)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:704)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:106)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:528)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:704)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:106)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:528)
    at com.esotericsoftware.kryo.Kryo.readObject(Kryo.java:704)
    at com.esotericsoftware.kryo.serializers.ObjectField.read(ObjectField.java:106)
    at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:528)
    at com.esotericsoftware.kryo.Kryo.readClassAndObject(Kryo.java:786)
    at SerializationTest.readObj(SerializationTest.java:39)
    at SerializationTest.main(SerializationTest.java:27)

我曾希望我可以序列化和反序列化PrintWriterobject outObj,并且对象的状态将保持不变,因此我仍然可以使用反序列化的对象来写入"Hello"本来会保存在缓冲区中的对象。

有谁知道发生了什么以及如何纠正这个错误?

4

2 回答 2

3

我认为,您想kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());避免构造函数调用。更多信息在这里

但是,如果我可能会问,你到底为什么要序列化 ​​a PrintWriter?那绝对是自找麻烦。Kryo 不是“灵丹妙药”,虽然它的默认反序列化器可以与大多数实用的类一起使用(即使这样,你也总是需要编写自定义插件),你当然不能指望它能够处理你能想出的每一个奇异的东西(并序列化由内部 jvm 特定代码支持的类,就像sun.*绝对有资格作为奇异一样)。

于 2015-01-01T19:56:48.497 回答
0

这个错误在 Kryo 中很常见。问题是 UTF_8 类不是公开的,因此 Kryo 失败了。添加下面的自定义序列化程序帮助我解决了它。与 Kryo 一起发布以下序列化程序是个好主意,因为很多人都在为此苦苦挣扎。

用于 UTF-8 和其他字符集的 kryo 自定义序列化程序

这样你就告诉 Kryo 对于所有注册的 Charset 类,调用我的自定义序列化程序。我只是在哪里发出字符串名称。

于 2015-09-11T16:19:39.293 回答