是否有一种方法来获得Java中当前正在执行的方法的名称?


Thread.currentThread().getStackTrace()通常会包含你调用它的方法,但有陷阱(参见Javadoc):

在某些情况下,一些虚拟机可能会从堆栈跟踪中省略一个或多个堆栈帧。在极端情况下,允许没有关于此线程的堆栈跟踪信息的虚拟机从此方法返回零长度数组。


2009年1月: 完整的代码将是(使用@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年12月更新:

蓝色的评论:

我使用JRE 6,给我不正确的方法名。 如果我写ste[2 + depth]. getmethodname(),它就会起作用。 0是getStackTrace(), 1是getMethodName(int depth)和 2是调用方法。

Virgo47的答案(获得好评)实际上计算了要应用的正确索引,以获得方法名。


从技术上讲,这是可行的……

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

但是,在编译时将创建一个新的匿名内部类(例如YourClass$1.class)。这将为每个部署这个技巧的方法创建一个。class文件。此外,在运行时的每次调用上都会创建一个未使用的对象实例。因此,这可能是一个可接受的调试技巧,但它确实带来了巨大的开销。

这种技巧的一个优点是getEnclosingMethod()返回java.lang.reflect.Method,该方法可用于检索方法的所有其他信息,包括注释和参数名。这使得区分具有相同名称的特定方法成为可能(方法重载)。

注意,根据getEnclosingMethod()的JavaDoc,这个技巧不应该抛出SecurityException,因为内部类应该使用相同的类装入器装入。因此,即使有安全管理人员在场,也不需要检查访问条件。

请注意:对于构造函数,必须使用getEnclosingConstructor()。在(命名)方法之外的块中,getEnclosingMethod()返回null。


我们使用这段代码来减少堆栈跟踪索引的潜在可变性-现在只需调用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不会做得那么糟糕。: -)


使用以下代码:

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

我发现最快的方法是:

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)。并将可访问的方法存储在静态变量中。


这是对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 + ")";
    }
}

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

Name的值为foo。


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

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

这种方法的错误之处在于:

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

另一种方法是创建(但不是抛出)一个Exception,并使用该对象从中获取堆栈跟踪数据,因为封闭方法通常位于索引0——只要JVM存储该信息,就像上面其他人提到的那样。然而,这并不是最便宜的方法。

从Throwable.getStackTrace()(这至少从Java 5开始是一样的):

数组的第0个元素(假设数组的长度非零)表示堆栈的顶部,这是序列中的最后一个方法调用。通常,这是这个throwable被创建和抛出的点。

下面的代码片段假设该类是非静态的(因为getClass()),但这只是题外话。

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

这两个选项都适合我使用Java:

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

Or:

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

要获取调用当前方法的方法的名称,您可以使用:

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

这在我的MacBook和我的Android手机上都可以使用

我还试过:

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

但是Android会返回"getStackTrace" 我可以修复这个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
    }
}

我有解决方案使用这个(在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();

我不知道获取当前执行的方法名称背后的意图是什么,但如果只是为了调试目的,那么像“logback”这样的日志框架可以在这里提供帮助。例如,在logback中,您只需要在日志配置中使用模式“%M”。但是,应该谨慎使用,因为这可能会降低性能。


如果您想知道的方法名称是junit测试方法,那么您可以使用junit TestName规则:https://stackoverflow.com/a/1426730/3076107


这可以使用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。


我把maklemenz的回答重写了一下

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

我使用这个代码片段与最新的Android Studio与最新的Java更新。它可以从任何活动、片段等调用。

public static void logPoint() {
    String[] splitPath = Thread.currentThread().getStackTrace()[3]
        .toString().split("\\.");

    Log.d("my-log", MessageFormat.format("{0} {1}.{2}",
        splitPath[splitPath.length - 3],
        splitPath[splitPath.length - 2],
        splitPath[splitPath.length - 1]
    ));
}

像这样叫它

logPoint();

输出

... D/my-log: MainActivity onCreate[(MainActivity.java:44)]