Ответ на этот вопрос сводится к тому, насколько вы контролируете код, который реализует Слушатель.Вы правы в том, что невозможно создать трассировку стека, не находясь в методе.
Общая техника заключается в создании Exception () в конструкторе, но не выбрасывает его.Он содержит информацию о трассировке стека, которую вы можете использовать по своему усмотрению.Это даст вам номер строки конструктора, но не класса.Обратите внимание, что этот метод также не особенно эффективен, поскольку создание трассировки стека стоит дорого.
Вам потребуется либо:
- принудительно выполнить некоторый код в конструкторе (относительнолегко, если ваш слушатель является абстрактным классом, которым вы управляете)
- Как-нибудь проработайте код (лекарство кажется здесь хуже, чем болезнь).
- Сделайте некоторые предположения о том, как называются классы.
- Прочитайте jar (сделайте то же самое, что и javac -p)
For 1), вы просто поместите создание исключения в абстрактный класс, и конструктор будет вызванподкласс:
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.Я не думаю, что это на самом деле указано где-либо, так что это, вероятно, просто соглашение, и на него не следует полагаться, если вы делаете что-то сложное с несколькими jvms и т. Д.
Это возможно, как 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 ()» находятся на разных строках.