Есть ли способ получить имя выполняемого в данный момент метода в Java?




Thread.currentThread().getStackTrace() обычно содержит метод, из которого вы его вызываете, но есть подводные камни (см. Javadoc):
Some virtual machines may, under some circumstances, omit one or more stack frames from the stack trace. In the extreme case, a virtual machine that has no stack trace information concerning this thread is permitted to return a zero-length array from this method.
Да, это. Документация для Метательный. [GetStackTrace ()] (download.oracle.com/javase/1.5.0 / docs / api / java / lang / … содержит точно такой же абзац.
В основе лежит то, что JVM - это не обязательный, чтобы иметь возможность обеспечивать трассировку стека, но большая работа была проделана для того, чтобы сделать HotSpot очень надежным. Однако вам нужно знать, если вы хотите, чтобы ваш код не зависел от поведения конкретной JVM.
Версия Alexsmail ниже не создает трассировку стека и дает вам доступ к фактическому объекту метода, а не только к имени (так что вы также можете узнать тип возвращаемого значения). Я не отмечал стендовую маркировку, но подозреваю, что его метод намного быстрее, так как трассировки стека, как правило, дороги.
Ответ Девина, кажется, дает гораздо более сжатый ответ на этот вопрос.
Январь 2009 г .:
Полный код будет (для использования с Предупреждение @ Bombe):
/**
* Get the method name for a depth in call stack. <br />
* Utility function
* @param depth depth in the call stack (0 means current method, 1 means call method, ...)
* @return method name
*/
public static String getMethodName(final int depth)
{
final StackTraceElement[] ste = Thread.currentThread().getStackTrace();
//System. out.println(ste[ste.length-depth].getClassName()+"#"+ste[ste.length-depth].getMethodName());
// return ste[ste.length - depth].getMethodName(); //Wrong, fails for depth = 0
return ste[ste.length - 1 - depth].getMethodName(); //Thank you Tom Tresansky
}
Больше в этот вопрос.
Обновление за декабрь 2011 г .:
голубоватый комментарии:
I use JRE 6 and gives me incorrect method name.
It works if I writeste[2 + depth].getMethodName().
0isgetStackTrace(),1isgetMethodName(int depth)and2is invoking method.
отвечатьдева47 (проголосовало за) фактически вычисляет правильный индекс для применения, чтобы вернуть имя метода.
Для меня это говорит только «главное». : - /
@Amigable: вы пытались распечатать весь массив StackTraceElement для целей отладки и проверить, действительно ли 'main' правильный метод?
Я использую JRE 6 и даю неправильное имя метода. Работает, если я напишу ste[2 + depth].getMethodName(). 0 - это getStackTrace(), 1 - это getMethodName(int depth), а 2 - вызывающий метод. См. Также @ virgo47 ответ.
@bluish: хороший момент. Я включил ваш комментарий и ссылку на ответ virgo47 в свой.
@VonC Действительно ли эта реализация верна? глубина здесь должна быть ste.length + 1, чтобы указать текущий метод. Разве не должно быть ste [глубина + 1], если мы должны разрешить глубину = 0?
@mmm Так было, когда я писал этот ответ одиннадцать много лет назад. Я упоминаю ответ virgo47 в моем собственном сообщении, и я поддержал ваш ответ.
@VonC Хорошо, спасибо. По какой причине вы вместо этого не редактируете свой ответ? В некоторых случаях это правильно?
@VonC Подождите, вы говорите, что JVM изменила реализацию и порядок трассировки стека в последних версиях JDK? Сначала я почувствовал, что моя память также сказала, что трассировки стека были перенесены. Теперь они сдвинуты. Странно, если это правда.
@mmm Я просто говорю, что не помню точное окружение, когда писал это. Но я признал, что другие ответы более точны.
Технически это сработает ...
String name = new Object(){}.getClass().getEnclosingMethod().getName();
Однако во время компиляции будет создан новый анонимный внутренний класс (например, YourClass$1.class). Таким образом, будет создан файл .class для каждого метода, использующего этот трюк. Кроме того, неиспользуемый экземпляр объекта создается при каждом вызове во время выполнения. Так что это может быть приемлемым приемом отладки, но он сопряжен со значительными накладными расходами.
Преимущество этого трюка в том, что getEnclosingMethod() возвращает java.lang.reflect.Method, который можно использовать для получения всей другой информации о методе, включая аннотации и имена параметров. Это позволяет различать определенные методы с одинаковым именем (перегрузка метода).
Обратите внимание, что согласно JavaDoc getEnclosingMethod() этот трюк не должен вызывать SecurityException, поскольку внутренние классы должны загружаться с использованием того же загрузчика классов. Таким образом, нет необходимости проверять условия доступа, даже если присутствует менеджер безопасности.
Пожалуйста, будьте осторожны: требуется использовать getEnclosingConstructor() для конструкторов. Во время блоков вне (именованных) методов getEnclosingMethod() возвращает null.
Прокомментируйте, поддерживается ли это во всех JVM и что такого ограничения, аналогичного упущению Stacktrace, не существует.
Это не даст вам метода, который в настоящее время выполняется. Это даст вам тот метод, в котором определен анонимный / локальный класс. - docs.oracle.com/javase/6/docs/api/java/lang/…
class Local {}; Имя строки = Local.class.getEnclosingMethod (). GetName ();
@ shrini1000 идея состоит в том, чтобы использовать этот фрагмент там, где необходима информация, а не помещать его в библиотечную процедуру.
@Devin Я вижу, что здесь также используется собственный API, например throwable. Итак, насколько быстро он по сравнению с другими методами, такими как StackTrace ..?
Будет ли это создавать анонимный внутренний класс во время компиляции, даже если вы поместите этот код в блок catch?
@AdamJensen, класс будет создан / скомпилирован во время компиляции, но экземпляры будут созданы только в том случае, если они будут выполнены в блоке catch.
Спасибо за советы! Вместо создания нового объекта просто используйте this.getClass (). GetEnclosingMethod (). GetName ();
А как насчет статических методов? "нестатическая переменная, на которую нельзя ссылаться из статического контекста"
можно ли получить значения параметров при вызове .getEnclosingMethod().getParameters()?
@wutzebaer Нет. Объект Method касается только метода и не имеет информации о текущем или каком-либо его вызове.
@ Лило неверно. getEnclosingMethod получает имя метода, в котором определен класс. this.getClass() вам совсем не поможет. @wutzebaer, зачем тебе это вообще нужно? У вас уже есть к ним доступ.
Мы использовали этот код для смягчения потенциальной изменчивости индекса трассировки стека - теперь просто вызовите methodName util:
public class MethodNameTest {
private static final int CLIENT_CODE_STACK_INDEX;
static {
// Finds out the index of "this code" in the returned stack trace - funny but it differs in JDK 1.5 and 1.6
int i = 0;
for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
i++;
if (ste.getClassName().equals(MethodNameTest.class.getName())) {
break;
}
}
CLIENT_CODE_STACK_INDEX = i;
}
public static void main(String[] args) {
System.out.println("methodName() = " + methodName());
System.out.println("CLIENT_CODE_STACK_INDEX = " + CLIENT_CODE_STACK_INDEX);
}
public static String methodName() {
return Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX].getMethodName();
}
}
Кажется, изощренно, но у нас было фиксированное число для JDK 1.5, и мы были немного удивлены, что оно изменилось, когда мы перешли на JDK 1.6. Теперь то же самое и в Java 6/7, но вы никогда не узнаете. Это не является доказательством изменений в этом индексе во время выполнения, но, надеюсь, HotSpot не так уж плох. :-)
Это все еще тонко зависит от поставщика. JVM не требуется для доставки надежных данных для этого кода.
Согласно спецификации JVM, JVM не требуется для обеспечения трассировки полного стека (оптимизация, встраивание и все такое), и вы уже обнаружили, что ваша эвристика изменилась между Oracle Java 5 и Oracle Java 6. Нет никаких гарантий, что любая другая JVM будет вести себя так, как вы ожидаете в своем коде, поэтому вы тонко полагаетесь на поведение конкретного поставщика. Что совершенно нормально, если вы об этом знаете, но если, например, вам нужно развернуть на IBM JVM (что мы должны) или на экземпляре Zing, вам, возможно, придется пересмотреть свою эвристику.
Это кажется наиболее надежным из всех представленных здесь вариантов, несмотря на зависимости.
Используйте следующий код:
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
StackTraceElement e = stacktrace[1];//coz 0th will be getStackTrace so 1st
String methodName = e.getMethodName();
System.out.println(methodName);
Это выводит на печать "getStackTrace" для меня - я использую Java 1.5.
будьте осторожны с пустым массивом в соответствии с ответом Bombe и указанием javadoc. Некоторые JVM могут не заполнять массив stacktrace?
Самый быстрый способ Я обнаружил, что:
import java.lang.reflect.Method;
public class TraceHelper {
// save it static to have it available on every call
private static Method m;
static {
try {
m = Throwable.class.getDeclaredMethod("getStackTraceElement",
int.class);
m.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getMethodName(final int depth) {
try {
StackTraceElement element = (StackTraceElement) m.invoke(
new Throwable(), depth + 1);
return element.getMethodName();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
Он напрямую обращается к собственному методу getStackTraceElement (int depth). И сохраняет доступный метод в статической переменной.
Самый быстрый по производительности? Какие-нибудь микро-тесты, подтверждающие это утверждение?
+1. Используя простой временной цикл на 1.6, 1000000 итераций с использованием этого метода заняли 1219 мс, в то время как использование new Throwable().getStackTrace() заняло 5614 мс.
m.setAccessible (правда); должен быть окружен AccessController.doPrivileged. Что-то, что нужно учитывать, я бы сказал, не жесткое правило
Протестировано в 2016 году и по-прежнему остается самым быстрым. Как и @ach, я использовал 1 млн итераций. 1.7_79: 1,6 с против 15,2 с 1,8_74: 1,8 с против 16,0 с. FWIW мой тестовый массив ste array length == 23, но этот метод остается быстрым независимо от глубины стека.
Это расширение на ответ virgo47 (выше).
Он предоставляет некоторые статические методы для получения текущих и вызывающих имен классов / методов.
/* Utility class: Getting the name of the current executing method
* https://stackoverflow.com/questions/442747/getting-the-name-of-the-current-executing-method
*
* Provides:
*
* getCurrentClassName()
* getCurrentMethodName()
* getCurrentFileName()
*
* getInvokingClassName()
* getInvokingMethodName()
* getInvokingFileName()
*
* Nb. Using StackTrace's to get this info is expensive. There are more optimised ways to obtain
* method names. See other stackoverflow posts eg. https://stackoverflow.com/questions/421280/in-java-how-do-i-find-the-caller-of-a-method-using-stacktrace-or-reflection/2924426#2924426
*
* 29/09/2012 (lem) - added methods to return (1) fully qualified names and (2) invoking class/method names
*/
package com.stackoverflow.util;
public class StackTraceInfo
{
/* (Lifted from virgo47's stackoverflow answer) */
private static final int CLIENT_CODE_STACK_INDEX;
static {
// Finds out the index of "this code" in the returned stack trace - funny but it differs in JDK 1.5 and 1.6
int i = 0;
for (StackTraceElement ste: Thread.currentThread().getStackTrace())
{
i++;
if (ste.getClassName().equals(StackTraceInfo.class.getName()))
{
break;
}
}
CLIENT_CODE_STACK_INDEX = i;
}
public static String getCurrentMethodName()
{
return getCurrentMethodName(1); // making additional overloaded method call requires +1 offset
}
private static String getCurrentMethodName(int offset)
{
return Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + offset].getMethodName();
}
public static String getCurrentClassName()
{
return getCurrentClassName(1); // making additional overloaded method call requires +1 offset
}
private static String getCurrentClassName(int offset)
{
return Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + offset].getClassName();
}
public static String getCurrentFileName()
{
return getCurrentFileName(1); // making additional overloaded method call requires +1 offset
}
private static String getCurrentFileName(int offset)
{
String filename = Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + offset].getFileName();
int lineNumber = Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX + offset].getLineNumber();
return filename + ":" + lineNumber;
}
public static String getInvokingMethodName()
{
return getInvokingMethodName(2);
}
private static String getInvokingMethodName(int offset)
{
return getCurrentMethodName(offset + 1); // re-uses getCurrentMethodName() with desired index
}
public static String getInvokingClassName()
{
return getInvokingClassName(2);
}
private static String getInvokingClassName(int offset)
{
return getCurrentClassName(offset + 1); // re-uses getCurrentClassName() with desired index
}
public static String getInvokingFileName()
{
return getInvokingFileName(2);
}
private static String getInvokingFileName(int offset)
{
return getCurrentFileName(offset + 1); // re-uses getCurrentFileName() with desired index
}
public static String getCurrentMethodNameFqn()
{
return getCurrentMethodNameFqn(1);
}
private static String getCurrentMethodNameFqn(int offset)
{
String currentClassName = getCurrentClassName(offset + 1);
String currentMethodName = getCurrentMethodName(offset + 1);
return currentClassName + "." + currentMethodName ;
}
public static String getCurrentFileNameFqn()
{
String CurrentMethodNameFqn = getCurrentMethodNameFqn(1);
String currentFileName = getCurrentFileName(1);
return CurrentMethodNameFqn + "(" + currentFileName + ")";
}
public static String getInvokingMethodNameFqn()
{
return getInvokingMethodNameFqn(2);
}
private static String getInvokingMethodNameFqn(int offset)
{
String invokingClassName = getInvokingClassName(offset + 1);
String invokingMethodName = getInvokingMethodName(offset + 1);
return invokingClassName + "." + invokingMethodName;
}
public static String getInvokingFileNameFqn()
{
String invokingMethodNameFqn = getInvokingMethodNameFqn(2);
String invokingFileName = getInvokingFileName(2);
return invokingMethodNameFqn + "(" + invokingFileName + ")";
}
}
В сочетании с ответом @mklemenz это очень быстрый и чистый способ доступа к информации о стеке.
public class SomeClass {
public void foo(){
class Local {};
String name = Local.class.getEnclosingMethod().getName();
}
}
name будет иметь значение foo.
Local.class.getEnclosingMethod () имеет значение null. jdk1.6.0_31, играть в 1.2.5
@eigil, это интересно, но без дополнительной информации трудно сказать, что пошло "не так" или когда нам следует ожидать null.
Это тот же трюк, что и этот ответ. Его преимущество в том, что он не создает поддельный экземпляр объекта, а недостаток в том, что он требует объявления класса, которое не может быть встроено в оператор (т.е. обычно требуется дополнительная строка кода).
@eigil вы определили класс внутри класса (пример SomeClass) или внутри метода (пример foo)? Я обнаружил, что определение подкласса без упаковки в метод или конструктор приведет к тому, что getEnclosingMethod () вернет значение null.
Совершенно уверен, что я сделал именно то, что описано в этом ответе. Я думаю, что с playframework что-то странно. Протестировано на "нормальной" java без проблем.
Это будет работать, только если метод статический.
String methodName =Thread.currentThread().getStackTrace()[1].getMethodName();
System.out.println("methodName = " + methodName);
См. Ответы mvanle virgo47 выше и комментарий thorbjorn-ravn-andersen. Повторяющийся, неточный и ненадежный код.
@ShivaKomuravelly Да, но, похоже, ни в какой ситуации, так что и от меня -1 тоже.
public static String getCurrentMethodName() {
return Thread.currentThread().getStackTrace()[2].getClassName() + "." + Thread.currentThread().getStackTrace()[2].getMethodName();
}
Да, безусловно, лучший ... превратите его в метод и получите третий ([2]) фрейм (или как он там называется) в трассировке.
Что плохого в таком подходе:
class Example {
FileOutputStream fileOutputStream;
public Example() {
//System.out.println("Example.Example()");
debug("Example.Example()",false); // toggle
try {
fileOutputStream = new FileOutputStream("debug.txt");
} catch (Exception exception) {
debug(exception + Calendar.getInstance().getTime());
}
}
private boolean was911AnInsideJob() {
System.out.println("Example.was911AnInsideJob()");
return true;
}
public boolean shouldGWBushBeImpeached(){
System.out.println("Example.shouldGWBushBeImpeached()");
return true;
}
public void setPunishment(int yearsInJail){
debug("Server.setPunishment(int yearsInJail = " + yearsInJail + ")",true);
}
}
И прежде чем люди сойдут с ума от использования System.out.println(...), вы всегда можете и должны создать какой-нибудь метод, чтобы вывод можно было перенаправить, например:
private void debug (Object object) {
debug(object,true);
}
private void dedub(Object object, boolean debug) {
if (debug) {
System.out.println(object);
// you can also write to a file but make sure the output stream
// ISN'T opened every time debug(Object object) is called
fileOutputStream.write(object.toString().getBytes());
}
}
@Saksham, мне кажется, это была попытка ответить на вопрос. Не лучшая попытка, но все же попытка.
@ivarni "плохая попытка"? что с этим не так? Вам знаком "принцип поцелуя"?
@Saksham, это было риторически.
@johnny В кодовой базе, которую я сейчас вижу, 271 класс. Даже с (низкой оценкой) 5 методов на класс, что составляет более 1300 методов. И это даже не большая кодовая база. Вы не видите проблемы с расширением вашего подхода? Я вполне счастлив согласиться с тем, что не согласен, но именно поэтому я сказал, что это плохая попытка. Это приводит к огромным накладным расходам в любой нетривиальной кодовой базе.
@ivarni, разве вам не нужно включать вызов метода в каждый метод (как предложено выше), чтобы выполнять ту же самую и МЕНЬШЕ надежную функцию?
@johnny Нет, если вы используете аспект.
На мой взгляд, основная проблема в том, что я буду повторяться. Это вредит ремонтопригодности. Теперь я не являюсь поклонником уловок с трассировкой стека, локального класса или исключений, поскольку они негативно влияют на производительность и иногда ненадежны для чего-то очень хорошего компилятора знает во время компиляции. Я лично пришел сюда в поисках чего-то вроде __func__, но подозреваю, что мы в конечном итоге остановимся на подходе Johnnys, поскольку он кажется наименее плохим.
@FuleSnabel "вредит ремонтопригодности"? вы говорите об одной строке кода для каждого метода, он всегда вызывается, вы всегда знаете, где находитесь, и вам не нужны сторонние утилиты. Я не понимаю, почему люди думают, что это главная проблема. он также отлично подходит для проверки параметров.
@johnny Думаю, я видел слишком много случаев, когда имя метода не соответствует строке, которая отправила меня в неправильном направлении при отладке. Но в Java я по-прежнему считаю ваше предложение лучшим, другие альтернативы «стоят» слишком дорого.
Альтернативный метод - создать, но не выбросить исключение и использовать этот объект для получения данных трассировки стека, поскольку включающий метод будет как правило с индексом 0 - пока JVM хранит эту информацию, как есть у других. упомянутое выше. Однако это не самый дешевый метод.
Из Throwable.getStackTrace () (как минимум, начиная с Java 5):
The zeroth element of the array (assuming the array's length is non-zero) represents the top of the stack, which is the last method invocation in the sequence. Typically, this is the point at which this throwable was created and thrown.
В приведенном ниже фрагменте предполагается, что класс нестатичен (из-за getClass ()), но это в стороне.
System.out.printf("Class %s.%s\n", getClass().getName(), new Exception("is not thrown").getStackTrace()[0].getMethodName());
Оба эти варианта работают для меня с Java:
new Object(){}.getClass().getEnclosingMethod().getName()
Или же:
Thread.currentThread().getStackTrace()[1].getMethodName()
для статических методов используйте: <Class> .class.getEnclosingMethod (). getName ()
будьте осторожны с пустым массивом в соответствии с ответом Bombe и указанием javadoc. Некоторые JVM могут не заполнять массив stacktrace?
Чтобы получить имя метода, который вызвал текущий метод, вы можете использовать:
new Exception("is not thrown").getStackTrace()[1].getMethodName()
Это работает на моем MacBook, а также на моем телефоне Android.
Я также пробовал:
Thread.currentThread().getStackTrace()[1]
но Android вернет "getStackTrace" Я мог бы исправить это для Android с помощью
Thread.currentThread().getStackTrace()[2]
но потом я получаю неправильный ответ на своем MacBook
В недавнем тестировании на Android мне показалось, что лучше использовать getStackTrace()[0], чем getStackTrace()[1]. YMMV.
вверх для android - это Thread.currentThread().getStackTrace()[2]
Util.java:
public static String getCurrentClassAndMethodNames() {
final StackTraceElement e = Thread.currentThread().getStackTrace()[2];
final String s = e.getClassName();
return s.substring(s.lastIndexOf('.') + 1, s.length()) + "." + e.getMethodName();
}
SomeClass.java:
public class SomeClass {
public static void main(String[] args) {
System.out.println(Util.getCurrentClassAndMethodNames()); // output: SomeClass.main
}
}
final StackTraceElement e = Thread.currentThread().getStackTrace()[2]; работает; e.getClassName(); возвращает полное имя класса, а e.getMethodName() возвращает имя метона.
getStackTrace()[2] неверен, это должен быть getStackTrace()[3], потому что: [0] dalvik.system.VMStack.getThreadStackTrace [1] java.lang.Thread.getStackTrace [2] Utils.getCurrentClassAndMethodNames [3] Функция a (), вызывающая этот
У меня есть решение, использующее это (в Android)
/**
* @param className fully qualified className
* <br/>
* <code>YourClassName.class.getName();</code>
* <br/><br/>
* @param classSimpleName simpleClassName
* <br/>
* <code>YourClassName.class.getSimpleName();</code>
* <br/><br/>
*/
public static void getStackTrace(final String className, final String classSimpleName) {
final StackTraceElement[] steArray = Thread.currentThread().getStackTrace();
int index = 0;
for (StackTraceElement ste : steArray) {
if (ste.getClassName().equals(className)) {
break;
}
index++;
}
if (index >= steArray.length) {
// Little Hacky
Log.w(classSimpleName, Arrays.toString(new String[]{steArray[3].getMethodName(), String.valueOf(steArray[3].getLineNumber())}));
} else {
// Legitimate
Log.w(classSimpleName, Arrays.toString(new String[]{steArray[index].getMethodName(), String.valueOf(steArray[index].getLineNumber())}));
}
}
MethodHandles.lookup().lookupClass().getEnclosingMethod().getName();
Пожалуйста, отредактируйте с дополнительной информацией. Ответы только на код и "попробуйте это" не приветствуются, потому что они не содержат доступного для поиска контента и не объясняют, почему кто-то должен "попробовать это".
Хотя этот код может помочь решить проблему, он не объясняет Зачем и / или как, он отвечает на вопрос. Предоставление этого дополнительного контекста значительно улучшило бы его долгосрочную образовательную ценность. Пожалуйста, редактировать свой ответ, чтобы добавить объяснение, в том числе, какие ограничения и предположения применяются.
Только для Java 7+, но краткий способ получить имя метода. Тем не менее, остается соображение производительности такого вызова.
getEnclosingMethod() бросает мне NullPointerException в Java 7.
Java.lang.Class.getEnclosingMethod () возвращает объект Method, представляющий непосредственно включающий метод базового класса, если этот объект Class представляет локальный или анонимный класс внутри метода, иначе возвращает null.
Каков след такого звонка?
Это неверный ответ. lookup().lookupClass() возвращает класс текущего метода, а getEnclosingMethod() возвращает метод, в котором объявлен текущий класс, если текущий класс является локальным классом. См. ideone.com/jfN5GW. Другими словами, для данного void foo() { class Local { void bar() { System.out.println(lookup().lookupClass().getEnclosingMethod()); } } new Local().bar(); } печатается метод foo() (потому что это включающий метод Local), но для этого вопроса нужен метод bar() (потому что это метод, который «выполняется в данный момент»).
Я не знаю, какова цель получения имени выполняемого в данный момент метода, но если это только для целей отладки, то здесь могут помочь фреймворки журналирования, такие как «logback». Например, при входе в систему все, что вам нужно сделать, это используйте шаблон "% M" в конфигурации ведения журнала. Однако это следует использовать с осторожностью, так как это может снизить производительность.
На всякий случай, если метод, имя которого вы хотите узнать, является тестовым методом junit, вы можете использовать правило junit TestName: https://stackoverflow.com/a/1426730/3076107
@AndreiKonstantinov Я не думаю, что это только ссылка. Даже если вы удалите ссылку, все равно останется как минимум информация некоторый.
Это можно сделать с помощью StackWalker начиная с Java 9.
public static String getCurrentMethodName() {
return StackWalker.getInstance()
.walk(s -> s.skip(1).findFirst())
.get()
.getMethodName();
}
public static String getCallerMethodName() {
return StackWalker.getInstance()
.walk(s -> s.skip(2).findFirst())
.get()
.getMethodName();
}
StackWalker спроектирован так, чтобы быть ленивым, поэтому он, вероятно, будет более эффективным, чем, скажем, Thread.getStackTrace, который нетерпеливо создает массив для всего стека вызовов. Также см. JEP для получения дополнительной информации.
Я немного переписал ответ Маклеменца:
private static Method m;
static {
try {
m = Throwable.class.getDeclaredMethod(
"getStackTraceElement",
int.class
);
}
catch (final NoSuchMethodException e) {
throw new NoSuchMethodUncheckedException(e);
}
catch (final SecurityException e) {
throw new SecurityUncheckedException(e);
}
}
public static String getMethodName(int depth) {
StackTraceElement element;
final boolean accessible = m.isAccessible();
m.setAccessible(true);
try {
element = (StackTraceElement) m.invoke(new Throwable(), 1 + depth);
}
catch (final IllegalAccessException e) {
throw new IllegalAccessUncheckedException(e);
}
catch (final InvocationTargetException e) {
throw new InvocationTargetUncheckedException(e);
}
finally {
m.setAccessible(accessible);
}
return element.getMethodName();
}
public static String getMethodName() {
return getMethodName(1);
}
Большинство ответов здесь кажется неправильным.
public static String getCurrentMethod() {
return getCurrentMethod(1);
}
public static String getCurrentMethod(int skip) {
return Thread.currentThread().getStackTrace()[1 + 1 + skip].getMethodName();
}
Пример:
public static void main(String[] args) {
aaa();
}
public static void aaa() {
System.out.println("aaa -> " + getCurrentMethod( ) );
System.out.println("aaa -> " + getCurrentMethod(0) );
System.out.println("main -> " + getCurrentMethod(1) );
}
Выходы:
aaa -> aaa
aaa -> aaa
main -> main
Спасибо за полезный ответ.
Не могли бы вы пояснить, почему большинство ответов кажется вам неправильным? Есть много ответов, и я не настолько хорошо разбираюсь в Java, чтобы прочитать их все и понять, в чем разница между ними и вашим ответом. :(
@mmm Извините, но я категорически не согласен. Я приехал сюда, чтобы учиться, и многие другие, как мне кажется, тоже. Я просто не могу понять, почему, по вашему мнению, я не заслуживаю более подробной информации по этому вопросу. Я хочу делать меньше ошибок в своем коде и предупреждать других, а не придерживаться какого-то карго-культа. Вы могли бы хотя бы уточнить, для какой версии Java этот код должен быть правильным. :( В ответе ниже говорится, что между 1.5 и 1.6 было изменение в stacktrace. Может быть, вы имеете в виду, что в грядущей Java 14 есть что-то подобное. Или это может быть у другого поставщика. Извините, если я неправильно истолковал ваш ответ как грубый один.
Верна ли эта же ошибка для трассировки стека в исключениях?