Sai*_*ali 16 java reflection debug-symbols
在给定java.lang.Class
对象的情况下,是否可以获取源文件名和声明类的行号?
数据应该在.class
文件的调试信息中提供.我知道的唯一的地方,JDK返回这样的调试信息,java.lang.StackTraceElement
但我不确定是否可以强制Java java.lang.StackTraceElement
为任意类创建实例,因为我们没有在类中执行方法.
我的确切用例是一个匿名内部类,它具有编译器生成的名称.我想知道类声明的文件名和行号.
我不想使用字节码操作框架,但如果必须的话我可以回到它.
这个问题的答案取决于您对实现监听器的代码有多少控制权。你是对的,如果不在方法中,就不可能创建堆栈跟踪。
一般技术是在构造函数中创建 Exception(),但不要抛出它。其中包含堆栈跟踪信息,您可以根据需要使用这些信息。这将为您提供构造函数的行号,但不是类的行号。请注意,此方法的性能也不是特别好,因为创建堆栈跟踪的成本很高。
您将需要:
对于 1),您只需将 Exception 创建放在抽象类中,构造函数就会由子类调用:
class Top {
Top() {
new Exception().printStackTrace(System.out);
}
}
class Bottom extends Top {
public static void main(String[] args) {
new Bottom();
}
}
Run Code Online (Sandbox Code Playgroud)
这会产生类似的结果:
java.lang.Exception
at uk.co.farwell.stackoverflow.Top.<init>(Top.java:4)
at uk.co.farwell.stackoverflow.Bottom.<init>(Bottom.java: 11)
at uk.co.farwell.stackoverflow.Bottom.main(Bottom.java: 18)
Run Code Online (Sandbox Code Playgroud)
一般来说,有一些命名规则需要遵循: 如果你有一个名为 Actor 的外部类和一个名为 Consumer 的内部类,那么编译后的类将被称为 Actor$Consumer。匿名内部类按照它们在文件中出现的顺序命名,因此 Actor$1 在文件中将出现在 Actor$2 之前。我认为这实际上并没有在任何地方指定,所以这可能只是一个约定,如果您正在使用多个 jvm 等执行任何复杂的操作,则不应依赖它。
正如 jmg 指出的那样,您可以在同一文件中定义多个顶级类。如果您有一个公共类 Foo,则必须在 Foo.java 中定义它,但非公共类可以包含在另一个文件中。上面的方法就可以解决这个问题。
解释:
如果你反汇编java(javap -c -verbose),你会看到调试信息中有行号,但它们只适用于方法。使用以下内部类:
static class Consumer implements Runnable {
public void run() {
// stuff
}
}
Run Code Online (Sandbox Code Playgroud)
javap 输出包含:
uk.co.farwell.stackoverflow.Actors$Consumer();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokespecial #10; //Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 20: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Luk/co/farwell/stackoverflow/Actors$Consumer;
Run Code Online (Sandbox Code Playgroud)
LineNumberTable 包含适用于方法的行号列表。所以我的 Consumer 构造函数从第 20 行开始。但这是构造函数的第一行,而不是类的第一行。它只是同一行,因为我使用的是默认构造函数。如果我添加一个构造函数,那么行号将会改变。编译器不存储声明该类的行。所以如果不解析java本身就无法找到类的声明位置。您根本没有可用的信息。
但是,如果您使用匿名内部类,例如:
Runnable run = new Runnable() {
public void run() {
}
};
Run Code Online (Sandbox Code Playgroud)
然后构造函数和类的行号将匹配[*],因此这会给您一个行号。
[*] 除非“new”和“Runnable()”位于不同的行。