Serialization/deserialization 与 GZIP 流不一致

Serialization/deserialization with GZIP Sterams isn't consitent

我有以下和平代码试图 serialize/deserialize a Throwable

public void test() throws IOException, ClassNotFoundException {
    IllegalStateException exception = new IllegalStateException("Oooops!");

    ByteBuffer seralized = serialize(exception);
    String asString = new String(seralized.array(), Charset.forName("UTF-8"));

    Throwable deserialized = deserialize(seralized);

    // false
    System.out.println(exception.equals(deserialized));
    // true
    System.out.println(exception.toString().equals(deserialized.toString()));

    seralized = serialize(deserialized);
    String toCompare = new String(seralized.array(), Charset.forName("UTF-8"));
    // true
    System.out.println(asString.equals(toCompare));
}

private Throwable deserialize(ByteBuffer seralized) throws IOException, ClassNotFoundException {
    return (Throwable) new ObjectInputStream(new GZIPInputStream(
            new ByteArrayInputStream(seralized.array()))).readObject();
}

private ByteBuffer serialize(Throwable exception) throws IOException {
    ByteArrayOutputStream causeBytesOut = new ByteArrayOutputStream();
    ObjectOutputStream causeOut = new ObjectOutputStream(
                                         new GZIPOutputStream(causeBytesOut));
    causeOut.writeObject(exception);
    causeOut.close();
    return ByteBuffer.wrap(causeBytesOut.toByteArray());
}

解释代码:我正在测试我的 serialization/deserialization 是否兼容。

第一个打印输出 (false) 告诉我反序列化后得到的结果与序列化后的结果不同。

第二个打印 (true) 表示对象 "somewhat" 相似。

我试图深入每个对象看看有什么区别,所以我再次序列化它并查看字节缓冲区的内容。根据上次打印(正确),这看起来是一样的。

为什么初始对象和经过 serialization/deserialization 的对象不同,尽管看起来是一样的?

请找到以下对您观察到的结果的解释。

  1. 对类型 IllegalStateException 的对象调用 toString 将只是 return 完全限定的 class 名称。这是来自 Throwable#toString()。因此,这对于两个对象都是相同的,equals 的结果将为真

    java.lang.IllegalStateException
    
  2. IllegalStateException 或其父项不会覆盖 equals 方法。因此将只等同于它自己。由于您的反序列化将创建一个新对象,因此您的对象在引用中不相等,因此 equals 将 return false(根据 Object#equals)。

  3. 您正在使用以下表达式以完全相同的方式创建 asStringtoCompare,同时由于这两个具有相同内容的不同字符串对象将等同,您的第三个表达式是真的。

    new String(seralized.array(), Charset.forName("UTF-8"));
    

如果您需要在使用原始对象调用 equals() 时让反序列化对象 return 为真,您可以子class IllegalStateException 并覆盖 equals 方法(在这种情况下也是 toString。)。

IllegalStateException 不会覆盖 Object.equals(),因此它通过 == 运算符使用对象标识。因此,对于不同的实例,它总是 return false。

您的测试无效。