在运行时获取成员字段的源顺序的保证方法?

gee*_*rt3 5 java reflection jvm

我正在寻找一种方法来检索(在运行时)源顺序中的类的字段,以便我可以执行自己的"初始化处理",这将基于声明的顺序.我知道Javadoc Class.getDeclaredFields()明确声明没有订单得到保证.

关于SO的一些答案指向Javassist但我没有找到任何证据表明javassist在没有行号信息的情况下有任何此类保证.

然而,这个"源顺序"由Java编译器使用,因为此代码无法编译:

private int a = 10 * b;
private int b = 5;
Run Code Online (Sandbox Code Playgroud)

显然,b目前尚不知道a正在宣布的价值.

这个初始化顺序也必须存在于字节码中,因为在运行时初始化必须以相同的顺序发生(被授予,这只是这些边缘情况的要求:-(但这使我认为自然的事情是存储.class文件中的源顺序.

问题:

  1. JVM/Byte Code如何以声明的顺序初始化成员字段,这些信息是否可以用于重建字段的源顺序?

  2. 有没有其他保证的方法来实现同样的目标.像Javassist这样的第三方工具可以,但必须"保证"或至少"在特定条件下保证".

  3. 是否有任何特定的Java实现可以保证顺序Class.getDeclaredFields()(可能在特定条件下(哪些))?

为了您的信息,我需要源订单来重建订单很重要的遗留语言的行为.我不喜欢明确添加顺序,例如通过添加数组或注释,因为我希望尽可能保持源的可读性.

- 编辑 - 一个重要的注意事项可能是我需要"遍历"的字段都将被注释,例如@MyComplexType(len = 4).父类将需要此元信息来构造一种内存映射.然而,我不想用订购信息来混淆这个注释,因为我发现这会妨碍可读性和可维护性.

Cla*_*oft 2

关于你的第二个问题和第三个问题,只能使用一种肮脏的黑客方法按顺序检索字段:

在字节码中,类文件的字段和方法都不是按顺序存储的。我不知道为什么会出现这种情况(尽管我制作了自己的 JVM 编译器),但我相信 Java 编译器只是决定这样做。Class.getDeclaredFields按照从字节码读取字段的顺序返回字段,这就是为什么它声明不保证顺序。

如果您仍然想让它们按顺序排列,我会尝试以下操作:您使用字节码解析器库(例如 Javassist 或 ASM)来读取类文件,并跳过除构造函数之外的所有内容(如果您还想对static {}静态字段进行排序) )。一旦遇到您正在检查的类的PUTFIELDorPUTSTATIC指令owner,您就可以通过字节码中存储的调试信息获取可用的当前行,并使用它对字段进行排序。这种技术的问题是效率低下,而且它依赖于行号属性,而这些属性并不总是存在于类文件中。此外,您只能找到PUT*显式初始化字段的说明,默认字段,例如

protected int modifiers;
Run Code Online (Sandbox Code Playgroud)

未由编译器初始化,因此字节码中没有可用的指令和行号信息。在这种情况下,或者当一般没有 LineNumber 属性时,您很不幸地运气不好。此时,我能想到的唯一解决方案就是阅读该类的实际源代码。

根据您尝试检查的类,您可能无法获取该类的实际字节码,但这本身就是一个问题。