我正在寻找一种方法(在运行时)按源代码顺序检索类的字段,以便我可以执行我自己的“初始化处理”,这将基于声明的顺序。我知道 Class.getDeclaredFields()
的 Javadoc 明确指出不保证任何顺序。
SO 上的一些答案指向 Javassist
但我找不到任何证据表明 javassist
在没有行号信息的情况下有任何此类保证。
然而,这个“源代码顺序”被 Java 编译器使用,因为这段代码无法编译:
private int a = 10 * b;
private int b = 5;
显然,b
的值在声明 a
时是未知的。
这个初始化顺序也必须出现在字节码中,因为在运行时初始化必须以相同的顺序发生(当然,这只是这些边缘情况的要求:-(然而这让我认为自然的事情会是将源代码顺序存储在 .class
文件中。
问题:
JVM/字节码如何按照声明的顺序初始化成员字段,这些信息是否可以用于重构字段的源顺序?
是否有任何其他有保证的方法可以实现同样的目标。像 Javassist 这样的第三方工具是可以的,但必须“保证”或至少“在特定条件下保证”。
是否有任何特定的 Java 实现可以保证
Class.getDeclaredFields()
的顺序(可能在特定条件下(哪些))?
供您引用,我需要源代码顺序来重建顺序很重要的遗留语言的行为。我不喜欢明确地添加订单,例如通过添加数组或注释,因为我希望尽可能保持源代码的可读性。
-- 编辑--
一个重要的注意事项可能是我需要“遍历”的字段都将被注释,例如@MyComplexType(len = 4)
。父类需要这个元信息来构造一种内存映射。然而,我不想将此注释与订购信息混为一谈,因为我发现这会妨碍可读性和可维护性。
最佳答案
关于你的第二个和第三个问题,只能使用一种肮脏的 hack 按顺序检索字段:
在字节码中,类文件的字段没有按顺序存储,方法也没有。我不知道为什么是这种情况(即使我制作了自己的 JVM 编译器),但我相信 Java 编译器只是决定这样做。 Class.getDeclaredFields
按照从字节码中读取字段的顺序返回字段,这就是为什么它声明不保证顺序的原因。
如果您仍想按顺序排列它们,我会尝试以下方法:您使用字节码解析器库(如 Javassist 或 ASM)来读取类文件,并跳过除构造函数(和 static { }
如果您还想对静态字段进行排序)。一旦遇到 PUTFIELD
或 PUTSTATIC
指令,其 owner
是您正在检查的类,您将获得通过调试可用的当前行存储在字节码中的信息,并用它来对字段进行排序。这种技术的问题是它的效率低下,而且它依赖于行号属性,而这些属性并不总是出现在类文件中。此外,您只会为显式初始化的字段找到 PUT*
指令,默认字段如
protected int modifiers;
没有被编译器初始化,所以字节码中没有可用的指令和行号信息。在这种情况下或通常没有 LineNumber 属性时,不幸的是你运气不好。那时,我唯一能想到的解决办法就是阅读该类的实际源代码。
根据您尝试检查的类,您可能无法获取类的实际字节码,但这本身就是一个问题。
关于java - 在运行时获取成员字段的源顺序的保证方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31046971/