Получение имени текущего метода выполнения

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

20 ответов


Thread.currentThread().getStackTrace() обычно будет содержать метод, из которого вы его вызываете, но есть подводные камни (см. Javadoc):

некоторые виртуальные машины могут при некоторых обстоятельствах пропустить один или несколько кадров стека из трассировки стека. В крайнем случае виртуальной машине, не имеющей сведений трассировки стека относительно этого потока, разрешается возвращать массив нулевой длины из этого метода.


технически это будет работать...

String name = new Object(){}.getClass().getEnclosingMethod().getName();

однако во время компиляции будет создан новый анонимный внутренний класс (например,YourClass.class). Так будет создано!-Файл -2--> для каждого метода, который развертывает этот трюк. Кроме того, при каждом вызове во время выполнения создается неиспользуемый экземпляр объекта. Таким образом, это может быть приемлемым трюком отладки, но он имеет значительные накладные расходы.

преимущество этого трюка в том, что getEncosingMethod() возвращает java.lang.reflect.Method который может использоваться для получения всей другой информации метода, включая аннотации и имена параметров. Это позволяет различать конкретные методы с одинаковым именем (перегрузка метода).

обратите внимание, что в соответствии с JavaDoc getEnclosingMethod() этот трюк не должен бросать SecurityException как внутренние классы должны быть загружены с помощью того же загрузчика классов. Поэтому нет необходимости проверять условия доступа, даже если присутствует менеджер безопасности.

это требуется использовать getEnclosingConstructor() для конструкторов. Во время блоков вне (именованных) методов,getEnclosingMethod() возвращает null.


января 2009 года:
Полный код будет (использовать с @машина это!--10--> в виду):

/**
 * 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:

иссиня комментарии:

Я использую JRE 6 и дает мне неправильное имя метода.
Это работает, если я пишу ste[2 + depth].getMethodName().

  • 0 и getStackTrace(),
  • 1 is getMethodName(int depth) и
  • 2 вызывает метод.

virgo47 ' s ответ (upvoted) фактически вычисляет правильный индекс для применения, чтобы вернуть имя метода.


мы использовали этот код для смягчения потенциальной изменчивости индекса трассировки стека-теперь просто вызовите 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 не делает этого плохо. :-)


 public class SomeClass {
   public void foo(){
      class Local {};
      String name = Local.class.getEnclosingMethod().getName();
   }
 }

name будет иметь значение foo.


самый быстрый способ Я нашел вот что:

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) напрямую. И сохраняет доступный метод в статической переменной.


использовать следующий код :

    StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
    StackTraceElement e = stacktrace[1];//coz 0th will be getStackTrace so 1st
    String methodName = e.getMethodName();
    System.out.println(methodName);

оба этих параметра работают для меня с Java:

new Object(){}.getClass().getEnclosingMethod().getName()

или:

Thread.currentThread().getStackTrace()[1].getMethodName()

public static String getCurrentMethodName() {
        return Thread.currentThread().getStackTrace()[2].getClassName() + "." + Thread.currentThread().getStackTrace()[2].getMethodName();
    }

Это расширение 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 + ")";
    }
}

получить имя метода, который вызвал текущий метод вы можете использовать:

new Exception("is not thrown").getStackTrace()[1].getMethodName()

это работает на моем MacBook, а также на моем телефоне Android

Я тоже пробовал:

Thread.currentThread().getStackTrace()[1]

но Android вернет " getStackTrace" Я мог бы исправить это для Android с

Thread.currentThread().getStackTrace()[2]

но тогда я получаю неправильный ответ на моем MacBook


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
    }
}

альтернативным методом является создание, но не исключение, и использование этого объекта для получения данных трассировки стека, так как метод enclosing будет обычно быть в индексе 0-до тех пор, пока JVM хранит эту информацию, как упоминалось выше. Однако это не самый дешевый метод.

С Throwable.getStackTrace() (это было то же самое с Java 5 по крайней мере):

нулевой элемент массива (при условии, что длина массива не равна нулю) представляет собой верхнюю часть стека, которая является последним вызовом метода в последовательности. обычно, это точка, в которой этот Throwable был создан и брошен.

приведенный ниже фрагмент предполагает, что класс нестатичен (из-за getClass ()), но это в стороне.

System.out.printf("Class %s.%s\n", getClass().getName(), new Exception("is not thrown").getStackTrace()[0].getMethodName());

String methodName =Thread.currentThread().getStackTrace()[1].getMethodName();
System.out.println("methodName = " + methodName);

Я не знаю, какое намерение стоит за получением имени текущего выполняемого метода, но если это только для цели отладки, то фреймворки ведения журнала, такие как "logback", могут помочь здесь. Например, в logback все, что вам нужно сделать, это используйте шаблон "%M " в конфигурации ведения журнала. Однако, это должно быть использовано с осторожностью по мере того как это может ухудшить представление.


У меня есть решение, использующее это (в 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())}));
    }
}

на случай, если метод, имя которого вы хотите знать, является методом теста junit, вы можете использовать правило junit TestName:https://stackoverflow.com/a/1426730/3076107


MethodHandles.lookup().lookupClass().getEnclosingMethod().getName();

Это можно сделать с помощью 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 для получения дополнительной информации.


что не так с этим подходом:

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(...) вы всегда можете и должны создать какой-то метод, чтобы вывод можно было перенаправить, e.g:

    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());
        }
    }