答案在于您对实现侦听器的代码有多少控制权。没错,没有方法就无法创建堆栈跟踪。
通用技术是在构造函数中创建Exception(),但不要抛出它。它包含堆栈跟踪信息,您可以根据需要使用它们。这将为您提供构造函数的行号,而不是类的行号。请注意,此方法也不是特别有效,因为创建堆栈跟踪非常昂贵。
您将需要:
- 强制在构造函数中执行一些代码(如果您的Listener是您控制的抽象类,则相对容易)
- 以某种方式检测代码(治愈似乎比这里的疾病还糟)。
- 对类的命名方式进行一些假设。
- 读取jar(与javac -p相同)
对于1),您只需将Exception创建物放在抽象类中,然后子类将调用构造函数:
class Top { Top() { new Exception().printStackTrace(System.out); }}class Bottom extends Top { public static void main(String[] args) { new Bottom(); }}这会产生类似:
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)
通常,遵循一些命名规则:如果您有一个称为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 }}并且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;
LineNumberTable包含适用于方法的行号列表。因此,我为Consumer使用的构造函数从第20行开始。但这是 构造函数
的第一行,而不是类的第一行。这只是同一行,因为我使用的是默认构造函数。如果添加构造函数,则行号将更改。编译器不存储声明该类的行。因此,如果不解析Java本身,就无法找到声明该类的位置。您只是没有可用的信息。
但是,如果您使用的是匿名内部类,例如:
Runnable run = new Runnable() { public void run() { }};然后,构造函数和类的行号将匹配[*],因此将为您提供一个行号。
[*]除非“ new”和“ Runnable()”在不同的行上。



