获取当前正在执行的方法的名称

Oma*_*eji 451 java reflection methods

有没有办法在Java中获取当前正在执行的方法的名称?

小智 298

从技术上讲,这将有效......

String name = new Object(){}.getClass().getEnclosingMethod().getName();
Run Code Online (Sandbox Code Playgroud)

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

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

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

它需要getEnclosingConstructor()用于构造函数.在(命名)方法之外的块期间,getEnclosingMethod()返回null.

  • @ shrini1000的想法是使用这个需要信息的片段,而不是把它放在库例程中. (20认同)
  • 这不会给你当前正在执行的方法.这将为您提供定义匿名/本地类的方法. - http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#getEnclosingMethod() (8认同)
  • class Local {}; String name = Local.class.getEnclosingMethod().getName(); (7认同)
  • 谢谢你的提示!不要创建新对象,只需使用this.getClass().getEnclosingMethod().getName(); (4认同)
  • @Lilo不对.`getEnclosingMethod`获取定义类的方法的名称.`this.getClass()`根本不会帮助你.@wutzebaer你为什么还需要?您已经可以访问它们了. (3认同)
  • 即使您将此代码放在catch块中,这是否会在编译期间创建一个匿名内部类? (2认同)

Bom*_*mbe 171

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

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

  • 是的.[Throwable]的文档(http://download.oracle.com/javase/1.5.0/docs/api/java/lang/Throwable.html).[getStackTrace()](http://download.oracle. com/javase/1.5.0/docs/api/java/lang/Throwable.html#getStackTrace(%29)包含完全相同的段落. (8认同)
  • 异常中的堆栈跟踪是否也存在同样的缺陷? (7认同)
  • 根本的事情是JVM不是_required_能够提供堆栈跟踪,但是很多工作已经使HotSpot非常可靠.但是,您需要知道,以防您希望代码不依赖于特定JVM的行为. (4认同)

Von*_*onC 135

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
}
Run Code Online (Sandbox Code Playgroud)

更多关于这个问题.

2011年12月更新:

蓝色评论:

我使用JRE 6并给出了错误的方法名称.
如果我写的话它会起作用ste[2 + depth].getMethodName().

  • 0是的getStackTrace(),
  • 1getMethodName(int depth)
  • 2 正在调用方法.

virgo47回答(upvoted)实际上计算了要应用的正确索引以便取回方法名称.

  • 我使用JRE 6并给出了错误的方法名称.如果我写'ste [2 + depth] .getMethodName()`,它就有效.0是`getStackTrace()`,1是`getMethodName(int depth)`,2是调用方法.另见[@ virgo47的答案](http://stackoverflow.com/a/8592871/505893). (7认同)
  • 它只对我说“主要”。:-/ (2认同)
  • @bluish:好点.我已将您的评论和对virgo47的答案的参考文献包括在内. (2认同)

vir*_*o47 83

我们使用此代码来缓解堆栈跟踪索引中的潜在可变性 - 现在只需调用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

似乎过度工程,但我们有一些JDK 1.5的固定数字,当我们转移到JDK 1.6时,它有点意外.现在它在Java 6/7中是相同的,但你永远不会知道.它不能证明在运行时期间该索引的更改 - 但希望HotSpot不会那么糟糕.:-)

  • 根据JVM规范,JVM不需要提供完整的堆栈跟踪(优化,内联和所有这些),并且您已经发现您的启发式在Oracle Java 5和Oracle Java 6之间发生了变化.没有任何保证任何其他JVM将会在您的代码中表现得如您所愿,因此您巧妙地依赖于供应商特定的行为.这是完全没问题,只要你知道这一点,但是如果 - 例如 - 你需要部署在IBM JVM(我们必须)或Zing实例上,你可能需要重新审视你的启发式. (6认同)
  • 这仍然微妙地依赖于供应商。JVM 不需要为此代码提供可靠的数据。 (2认同)

ale*_*ail 45

 public class SomeClass {
   public void foo(){
      class Local {};
      String name = Local.class.getEnclosingMethod().getName();
   }
 }
Run Code Online (Sandbox Code Playgroud)

名称将具有值foo.

  • Local.class.getEnclosingMethod()为null.jdk1.6.0_31,播放1.2.5 (4认同)

Cha*_*man 33

这两个选项都适用于Java:

new Object(){}.getClass().getEnclosingMethod().getName()
Run Code Online (Sandbox Code Playgroud)

要么:

Thread.currentThread().getStackTrace()[1].getMethodName()
Run Code Online (Sandbox Code Playgroud)


mak*_*enz 32

我找到的最快的方法是:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它直接访问本机方法getStackTraceElement(int depth).并将可访问的Method存储在静态变量中.

  • +1.使用简单的定时循环1.6,1,000,000次迭代使用此方法需要1219ms,而使用`new Throwable().getStackTrace()`需要5614ms. (9认同)
  • 在2016年进行了测试,这仍然是最快的.像@ach一样,我使用了1M次迭代.1.7_79:1.6s vs 15.2s 1.8_74:1.8s vs 16.0s.FWIW我的基准ste数组长度== 23但是无论堆栈深度如何,此方法都保持快速. (5认同)
  • 表现最快的是最快的吗?任何支持索赔的微观基准? (3认同)

Sum*_*ngh 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);
Run Code Online (Sandbox Code Playgroud)

  • 这打印出"getStackTrace"对我来说 - 我正在使用Java 1.5 (2认同)

小智 16

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


mva*_*nle 14

这是对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 + ")";
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这与@ mklemenz的答案相结合,是一种访问堆栈信息的快速而简洁的方法. (3认同)

Fri*_*ade 12

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

new Exception("is not thrown").getStackTrace()[1].getMethodName()
Run Code Online (Sandbox Code Playgroud)

这适用于我的MacBook以及我的Android手机

我也尝试过:

Thread.currentThread().getStackTrace()[1]
Run Code Online (Sandbox Code Playgroud)

但Android将返回"getStackTrace"我可以用Android修复此问题

Thread.currentThread().getStackTrace()[2]
Run Code Online (Sandbox Code Playgroud)

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


Max*_*ple 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();
}
Run Code Online (Sandbox Code Playgroud)

SomeClass.java:

public class SomeClass {
    public static void main(String[] args) {
        System.out.println(Util.getCurrentClassAndMethodNames()); // output: SomeClass.main
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Rad*_*def 7

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();
}
Run Code Online (Sandbox Code Playgroud)

StackWalker被设计为懒惰的,所以它可能比Thread.getStackTrace为整个调用栈急切创建一个数组的效率更高。另请参阅JEP以获取更多信息。

  • 现在应该是公认的答案。 (4认同)

Joo*_*ool 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());
Run Code Online (Sandbox Code Playgroud)