ajc 和 javac 生成的 class 之间的序列化不兼容

Serialization incompatibility between class generated by ajc and javac

最近发现有些Java(Java8)和ajc(v.1.9.2)编译的classes不是序列化-兼容。 serialization-compatibility 我的意思是计算出的默认 serialVersionUID 不相同。

示例:

public class Markup implements Serializable {
    private final MyUnit unit;
    public Markup(MyUnit unit) { this.unit = unit; }

    public enum MyUnit { DOUBLE, STRING }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Path path = Paths.get("markup.bin");
        if (args.length == 0) {
            try (OutputStream fileOutput = Files.newOutputStream(path);
                 ObjectOutputStream objectOutput = new ObjectOutputStream(fileOutput))
            {
                objectOutput.writeObject(new Markup(MyUnit.STRING));
            }
        } else {
            try (InputStream fileInput = Files.newInputStream(path);
                 ObjectInputStream objectInput = new ObjectInputStream(fileInput))
            {
                System.out.println(objectInput.readObject());
            }
        }
    }

    static String switchType(MyUnit unit) {
        switch (unit) {
            case STRING: return "%";
            case DOUBLE: return "p";
            default: return "Undefined";
        }
    }
}

当我用 ajc 和 运行 编译这个 class,然后用 javac 和 运行 编译这个 class 时,我得到关于不兼容的异常序列化格式:

Exception in thread "main" java.io.InvalidClassException: Markup; local class incompatible: stream classdesc serialVersionUID = -1905477862550005139, local class serialVersionUID = 793529206923536473

我也发现这是因为ajc 开关代码生成器。它在 class private static int[] $SWITCH_TABLE$Markup$MyUnit

中创建附加字段

javac 生成的字段: ajc生成的字段:

我的问题是:

  1. 规范是否允许 java 编译器生成 class 中未定义的字段?
  2. 为什么ajc会生成额外的字段?某种性能优化?
  3. 有什么方法可以让 ajc 不产生额外的字段吗?
  4. private static影响serialVersionUID生成的原因是什么?
  5. aspectj 的开发者是否知道这种行为?如果是这样,为什么他们仍然选择生成字段?
  6. 是否可以保证 Java class 将如何被 JLS 序列化?
  7. 没有这个字段,Javac-generated 代码如何工作?

1. java 编译器规范是否允许生成 class 中未定义的字段?

是的,尽管它们应该标记为 合成。 Synthetics 包括内部 classes 的访问器(尽管该实现最近发生了变化)、lambda 表达式方法以及我认为的默认构造函数。

2。为什么 ajc 会生成额外的字段?某种性能优化?

看起来像是一个糟糕的性能优化。可能跟有效添加aspect有关。

3。有没有办法让ajc不产生额外的字段?

不知道。您应该期望无论如何都会生成合成材料。

4. private static 影响 serialVersionUID 生成的原因是什么?

Java 序列化是在 "Internet time" 中创建的。为了兼容性,我们只剩下第一个版本碰巧做了什么。道德:如果你在互联网时代创造了任何东西,请把它扔掉。

5. aspectj 的开发者是否知道这种行为?如果是这样,为什么他们仍然选择生成字段?

我希望如此。编译器应该生成合成词。

6.是否有任何保证 Java class 将如何被 JLS 序列化?

有一个 Java 序列化规范(虽然我真的不希望有多少人阅读它)。

7. Javac 生成的代码如何在没有此字段的情况下工作?

要了解如何在没有数组的情况下编写 switch-on-string 相对容易。丑陋的优化版本的确切细节将变得混乱。你可以看到 javap -private -c.

实际发生了什么

结论

如果希望在不同版本之间使用数据,建议添加一个serialVersionUID class。 OTOH,还建议不要使用 Java 序列化。