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

时间:2009-01-14 12:20:39

标签: java reflection methods

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

22 个答案:

答案 0 :(得分:289)

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

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

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

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

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

需要使用getEnclosingConstructor()作为构造函数。在(命名)方法之外的块期间,getEnclosingMethod()返回null

答案 1 :(得分:164)

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

  

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

答案 2 :(得分:134)

2009年1月:
一个完整的代码(与@Bombe's caveat一起使用):

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

更多this question

2011年12月更新:

bluish评论:

  

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

,它就有效      
      
  • 0getStackTrace()
  •   
  • 1getMethodName(int depth)
  •   
  • 2正在调用方法。
  •   

virgo47answer(upvoted)实际上会计算要应用的正确索引,以便取回方法名称。

答案 3 :(得分:82)

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

答案 4 :(得分:42)

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

名称将具有值foo。

答案 5 :(得分:30)

我找到的最快的方式是:

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

答案 6 :(得分:29)

这两个选项对我来说都适用于Java:

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

或者:

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

答案 7 :(得分: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);

答案 8 :(得分:15)

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

答案 9 :(得分:13)

这是virgo47's answer上面的扩展(上图)。

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

/* 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 + ")";
    }
}

答案 10 :(得分:10)

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

答案 11 :(得分:4)

另一种方法是创建但不抛出异常,并使用该对象从中获取堆栈跟踪数据,因为封闭方法 通常 在索引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());

答案 12 :(得分:4)

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

答案 13 :(得分: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())}));
    }
}

答案 14 :(得分:3)

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

答案 15 :(得分:3)

我不知道获取当前执行方法名称的目的是什么,但如果这只是为了调试目的,那么记录框架就像&#34; logback&#34;可以帮到这里。例如,在logback中,您需要做的只是use the pattern "%M" in your logging configuration。但是,应谨慎使用,因为这可能会降低性能。

答案 16 :(得分:3)

从Java 9开始,可以使用StackWalker完成此操作。

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更有效,extension UINavigationController { func pushToViewController(_ viewController: UIViewController, animated:Bool = true, completion: @escaping ()->()) { CATransaction.begin() CATransaction.setCompletionBlock(completion) self.pushViewController(viewController, animated: animated) CATransaction.commit() } func popViewController(animated:Bool = true, completion: @escaping ()->()) { CATransaction.begin() CATransaction.setCompletionBlock(completion) self.popViewController(animated: true) CATransaction.commit() } func popToViewController(_ viewController: UIViewController, animated:Bool = true, completion: @escaping ()->()) { CATransaction.begin() CATransaction.setCompletionBlock(completion) self.popToViewController(viewController, animated: animated) CATransaction.commit() } func popToRootViewController(animated:Bool = true, completion: @escaping ()->()) { CATransaction.begin() CATransaction.setCompletionBlock(completion) self.popToRootViewController(animated: animated) CATransaction.commit() } } 渴望为整个调用堆栈创建一个数组。 Also see the JEP for more information.

答案 17 :(得分:2)

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

答案 18 :(得分:0)

我重写了maklemenz's answer

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

答案 19 :(得分:0)

这里的大多数答案似乎是错误的。

    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

答案 20 :(得分:0)

我将此代码片段与具有最新 Java 更新的最新 Android Studio 结合使用。它可以从任何 Activity、Fragment 等中调用。

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)]

答案 21 :(得分:-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());
        }
    }