最近我发现Java(Java 8)和ajc(v.1.9.2)编译的某些类不兼容 serialization 。所谓序列化兼容性,是指计算出的默认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
我的问题是:
private static
影响serialVersionUID生成的原因是什么?答案 0 :(得分:3)
1。规范允许Java编译器生成类中未定义的字段吗?
是的,尽管应该将它们标记为 synthetic 。合成器包括内部类的访问器(尽管实现方式最近有所变化),lambda表达式方法以及默认的构造函数。
2。为什么AJC会生成其他字段?某种性能优化?
性能优化不佳。也许与有效添加方面有关。
3。有什么方法可以使AJC不生成其他字段?
不知道。您应该期望能够合成合成物。
4。私有静态会影响serialVersionUID生成的原因是什么?
Java序列化是在“ Internet time”中创建的。为了兼容,我们保留了第一个版本要做的任何事情。道德:如果您在互联网时代创造了任何东西,请扔掉它。
5。 Aspectj的开发人员是否知道此行为?如果是这样,为什么他们仍然选择生成字段?
我希望如此。编译器有望成为合成材料。
6。是否可以保证JLS如何序列化Java类?
有一个Java序列化规范(尽管我真的不希望很多人阅读它)。
7。没有此字段,Javac生成的代码如何工作?
比较容易看出如何在不使用数组的情况下编写字符串转换字符串。丑陋的优化版本的确切细节将变得混乱。您可以看到javap -private -c
实际发生了什么。
结论
如果期望在类的不同版本之间使用数据,建议添加serialVersionUID
。 OTOH,也建议不要使用Java序列化。