【发布时间】:2019-09-17 13:56:47
【问题描述】:
最近我发现一些由 Java (Java 8) 和 ajc (v.1.9.2) 编译的类不兼容序列化。序列化兼容性是指计算出的默认 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 编译这个类并运行,然后通过 javac 编译这个类并运行时,我得到关于序列化格式不兼容的异常:
Exception in thread "main" java.io.InvalidClassException: Markup; local class incompatible: stream classdesc serialVersionUID = -1905477862550005139, local class serialVersionUID = 793529206923536473
我还发现这是因为 ajc 开关代码生成器。它在 private static int[] $SWITCH_TABLE$Markup$MyUnit 类中创建附加字段
我的问题是:
- Java 编译器规范是否允许生成类中未定义的字段?
- 为什么 ajc 会生成额外的字段?某种性能优化?
- 有没有办法让ajc不生成额外的字段?
-
private static影响 serialVersionUID 生成的原因是什么? - aspectj 的开发人员是否知道这种行为?如果是这样,为什么他们仍然选择生成字段?
- 是否有任何保证 JLS 将如何序列化 Java 类?
- 没有这个字段,Javac 生成的代码如何工作?
【问题讨论】:
-
1.是的。 2. 看起来像一个开关表。 3. 没有。 4. 不清楚。你需要自己指定
serialVersionUID来解决这个问题。 -
5.见 4。 6. 是的,有一个完整的对象序列化规范。 7. 太宽泛。你总是可以按照我建议的方式尝试,而不是仅仅添加更多毫无意义的问题。每个帖子一个问题是这里通常的最大值。
-
>6.是的,有一个完整的对象序列化规范。为什么在这种情况下它不能很好地工作?如果有规范 - 这意味着某些编译器会忽略它。哪一个?我想知道更深入的细节。不仅仅是是/否的答案。
-
我不认为澄清问题毫无意义。它们有助于解释背景——我不想知道的。
-
见§4.6 of the Java Object Serialization Specification;它明确警告默认
serialVersionUID计算对编译器实现高度敏感。它还解释了如何计算默认值。请注意,Serializable文档也有相同的警告。
标签: java serialization aspectj javac serialversionuid