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


当前回答

这是对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。

另一种方法是创建(但不是抛出)一个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());

我把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);
}

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的答案(获得好评)实际上计算了要应用的正确索引,以获得方法名。