获取当前执行方法的名称


Answers:


177

Thread.currentThread().getStackTrace()通常会包含您从中调用它的方法,但是存在一些陷阱(请参阅Javadoc):

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


7
异常中的堆栈跟踪是否也有同样的陷阱?
内特·帕森斯2009年

8
是的。Throwable。[getStackTrace()](download.oracle.com/javase/1.5.0/docs/api/java/lang/…的文档包含完全相同的段落
。– Bombe

4
根本的问题是,不需要 JVM 能够提供堆栈跟踪,但是使HotSpot非常可靠的工作量很大。但是,您需要知道,以防您的代码不依赖特定JVM的行为。
托尔比约恩Ravn的安徒生

下面的Alexsmail版本不会创建堆栈跟踪,它使您可以访问实际的方法对象,而不仅是名称(因此您也可以找到返回类型)。我没有进行基准测试,但我怀疑他的方法也快得多,因为堆栈跟踪往往很昂贵。
2013年

德文的回答似乎对这个问题给出了更为简洁的答案。
上升潮'16

310

从技术上讲,这将起作用...

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

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

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

请注意,根据JavaDoc的getEnclosingMethod()技巧,不应SecurityException使用一个相同的类加载器来加载内部类。因此,即使存在安全管理器,也无需检查访问条件。

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


9
这不会给您当前正在执行的方法。这将为您提供定义匿名/本地类的方法。- docs.oracle.com/javase/6/docs/api/java/lang/...
shrini1000

7
类Local {}; 字符串名称= Local.class.getEnclosingMethod()。getName();
alexsmail 2013年

21
@ shrini1000的想法是在需要信息的地方使用此代码段,而不是将其放在库例程中。
托尔比约恩Ravn的安徒生

4
感谢您的提示!无需创建新对象,只需使用this.getClass()。getEnclosingMethod()。getName();
Lilo 2015年

3
@Lilo不正确。getEnclosingMethod获取定义类的方法的名称。this.getClass()完全不会帮助您 @wutzebaer为什么还要?您已经可以访问它们。
Hazel Troost

134

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().

  • 0getStackTrace()
  • 1getMethodName(int depth)
  • 2 是调用方法。

virgo47答案(建议)实际上是计算要应用的正确索引,以便取回方法名称。


2
它只对我说“主要”。:-/
Falken教授的合同

@Amigable:您是否尝试打印所有StackTraceElement数组以进行调试,并查看“ main”是否实际上是正确的方法?
VonC

7
我使用JRE 6,并给我错误的方法名称。如果我写的话可以用ste[2 + depth].getMethodName()。0是getStackTrace(),1是getMethodName(int depth),2是调用方法。另请参阅@ virgo47的答案
蓝蓝的

2
@蓝蓝:好点。我已经在我的文章中加入了您的评论和对virgo47答案的引用。
VonC

@VonC这个实现真的正确吗?此处的depth必须为ste.length +1才能给出当前方法。如果我们允许depth = 0,它应该不是ste [depth + 1]吗?
mmm

85

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


1
这仍然取决于供应商。不需要JVM为该代码提供可靠的数据。
托尔比约恩Ravn的安徒生

6
根据JVM规范,不需要JVM提供完整的堆栈跟踪(优化,内联等等),并且您已经发现您的启发式在Oracle Java 5和Oracle Java 6之间发生了变化。无法保证其他任何JVM都会行为符合您在代码中的预期,因此您将巧妙地依赖于供应商特定的行为。只要您知道这一点就可以了,但是,例如,如果您需要在IBM JVM(我们必须)或Zing实例上进行部署,则可能不得不重新尝试一下启发式方法。
托尔比约恩Ravn的安徒生

1
尽管有依赖关系,但这似乎是这里介绍的所有选项中最强大的。
伊恩

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

名称将具有值foo。


5
Local.class.getEnclosingMethod()为空。jdk1.6.0_31,播放1.2.5
eigil 2013年

@eigil很有趣,但没有更多信息,很难说出哪里出了问题或我们应该期待什么null
Maarten Bodewes 2014年

这和这个答案是一样的把戏。它的优点是它不会创建伪造的对象实例,它的缺点是它需要一个不能在语句中内联的类声明(即,通常需要附加的代码行)。
Maarten Bodewes 2014年

@eigil是否在类(例如SomeClass)或方法(例如foo)中定义了类?我发现定义子类而不包装在方法或构造函数中将导致getEnclosingMethod()返回null。
DN

可以肯定,我确实按照此答案中的描述进行了操作。我认为Playframework有点奇怪。在“正常”的Java中进行了测试,没有任何问题。
eigil 2015年

36

这两个选项都对Java有效:

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

要么:

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

1
对于静态方法,请使用:<Class> .class.getEnclosingMethod()。getName()
jellobird

根据Bombe的答案和javadoc的指示小心空数组。某些JVM可能无法填充stacktrace数组?
el-teedee

34

我发现最快的方法是:

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


3
在性能方面最快吗?有任何微基准可以支持该主张吗?
Ibrahim Arief 2012年

10
+1。在1.6上使用简单的定时循环,使用此方法进行的1,000,000次迭代花费了1219ms,而使用new Throwable().getStackTrace()了5614ms。
2013年

1
m.setAccessible(true); 应该被AccessController.doPrivileged包围。要考虑的事情,而不是一个硬性的规则,我会说
avanderw

6
经过2016年测试,并且仍然是最快的。像@ach一样,我使用了1M次迭代。1.7_79:1.6s和15.2s 1.8_74:1.8s和16.0s。FWIW我的基准ste数组的长度== 23,但是不管堆栈深度如何,此方法都保持快速。
瑞安

25

使用以下代码:

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

2
这为我打印了“ getStackTrace”-我正在使用Java 1.5
Zack Macomber

根据Bombe的答案和javadoc的指示小心空数组。某些JVM可能无法填充stacktrace数组?
el-teedee

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

是的,到目前为止最好的...将其转换为方法,并在跟踪中获得第三帧([2])(或任何它称为的帧)。
麦克啮齿动物,

14

这是virgo47答案的扩展(上)。

它提供了一些静态方法来获取当前和正在调用的类/方法名称。

/* Utility class: Getting the name of the current executing method 
 * /programming/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. /programming/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 + ")";
    }
}

3
结合@mklemenz的答案,这是一种访问堆栈信息的非常快速,干净的方法。
Octavia Togami,

12

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

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

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

我也尝试过:

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

但是Android会返回“ getStackTrace”,我可以使用

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

但是我在MacBook上得到了错误的答案


在Android上最近进行的测试中,它比对我而言getStackTrace()[0]更有效getStackTrace()[1]。YMMV。
mbm29414 '16

适用于Android的游戏是Thread.currentThread().getStackTrace()[2]
Ninja

11

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()返回methon名称。
2015年

1
getStackTrace()[2]是错误的,这必须是getStackTrace()[3]因为:[0] dalvik.system.VMStack.getThreadStackTrace [1] java.lang.Thread.getStackTrace [2] Utils.getCurrentClassAndMethodNames [3]函数a()调用此函数
PhilLab

11

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以获取更多信息。


5

一种替代方法是创建但不引发Exception,并使用该对象从中获取堆栈跟踪数据,因为封闭方法通常会位于索引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());

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

1
请参阅上面的mvanle virgo47的答案和thorbjorn-ravn-andersen的评论。重复,不准确和不可靠的代码。
alexsmail 2013年

@ShivaKomuravelly是的,但似乎在任何情况下都没有,所以我也为-1。
Maarten Bodewes 2014年

3

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


2

万一您要知道的名称方法是junit测试方法,则可以使用junit TestName规则:https ://stackoverflow.com/a/1426730/3076107


1
@AndreiKonstantinov我认为这不是仅链接。即使您删除了链接,仍然至少还有一些信息要继续。
EJoshuaS-恢复莫妮卡

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

感谢您的有用回答。
AmerllicA

您能否阐明为什么大多数答案对您来说都是错误的?有很多答案,而我对Java并不精通,因此无法阅读所有答案并了解它们与您的答案有什么区别。:(
Xobotun

@mmm对不起,但我强烈不同意。我相信,我是来这里学习的,其他许多人也是如此。我只是不明白为什么您认为我不应该在这个问题上了解更多。我想在我的代码中减少错误,并警告其他人,而不是遵循某些狂热的习惯。您至少可以弄清楚该代码应该在哪个Java版本上正确。:(下面的回答说,堆栈跟踪在1.5和1.6之间发生了变化。也许您暗示即将到来的Java 14中有类似的事情,我怎么知道。或者其他供应商可能有。抱歉,如果我误认为您的回答是很粗鲁的一个
Xobotun

0

我重写了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);
}

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

11
请编辑更多信息。不建议使用仅代码和“尝试此”答案,因为它们不包含可搜索的内容,并且不解释为什么有人应该“尝试此”。
abarisone

1
尽管此代码可能有助于解决问题,但并未解释为什么和/或如何回答问题。提供这种额外的环境将大大提高其长期教育价值。请编辑您的答案以添加解释,包括适用的限制和假设。
Toby Speight

1
仅适用于Java 7+,但简洁的方法来获取方法名称。仍然保持这种呼叫的性能考虑。
Benj

6
getEnclosingMethod()抛出一个NullPointerException在Java 7中对我来说
马库斯大号

2
如果此Class对象表示方法中的本地或匿名类,则java.lang.Class.getEnclosingMethod()返回表示基础类的立即封闭方法的Method对象。

-5

这种方法有什么问题:

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

4
@Saksham在我看来实际上是试图回答这个问题。这不是一次伟大的尝试,但仍然是一次尝试。
ivarni 2014年

@ivarni“不好尝试”吗?它出什么问题了?您是否熟悉“亲吻原则”?
约翰尼

@Saksham这很夸张。
约翰尼

5
@johnny在我面前的代码库中,现在有271个类。即使每类使用(低估)og 5种方法,也超过1300种方法。而且这甚至不是很大的代码库。您看不到扩展方法的问题吗?我很高兴同意不同意,但这就是为什么我说这不是一个好尝试。它在任何非平凡的代码库中都会带来大量的开销。
ivarni 2014年

1
@johnny我想我已经看到了太多的情况,其中方法名称与字符串不匹配,这在调试时向错误的方向发送了我。但是,在Java中,我仍然认为您的建议是最好的,其他选择会“花费”太多。
只是另一位元编程人员
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.