关于java:获取当前正在执行的方法的名称

Getting the name of the currently executing method

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


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

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

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

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

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

施工人员使用getEnclosingConstructor()。在(命名)方法之外的块期间,getEnclosingMethod()返回null


Thread.currentThread().getStackTrace()通常包含您从中调用它的方法,但存在缺陷(请参见javadoc):

Some virtual machines may, under some circumstances, omit one or more stack frames from the stack trace. In the extreme case, a virtual machine that has no stack trace information concerning this thread is permitted to return a zero-length array from this method.


2009年1月:完整的代码应该是(考虑到@bombe的警告):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
 * 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月更新:

蓝色评论:

I use JRE 6 and gives me incorrect method name.
It works if I write ste[2 + depth].getMethodName().

  • 0 is getStackTrace(),
  • 1 is getMethodName(int depth) and
  • 2 is invoking method.

Virgo47的答案(上票)实际上计算了要应用的正确索引,以便返回方法名。


我们使用此代码来减轻堆栈跟踪索引中的潜在变化性-现在只需调用methodname-util:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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();
    }
}

似乎设计得太过分了,但JDK1.5有一些固定的数字,当我们移到JDK1.6时,它发生了一些变化,这让我们有点惊讶。现在在Java 6/7中是一样的,但是你永远不知道。它并不能证明在运行时该索引发生了变化,但希望Hotspot不会那么糟糕。-)


1
2
3
4
5
6
 public class SomeClass {
   public void foo(){
      class Local {};
      String name = Local.class.getEnclosingMethod().getName();
   }
 }

名称将具有值foo。


这两个选项都适用于Java:

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

或:

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


我发现最快的方法是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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)。并将可访问方法存储在静态变量中。


使用以下代码:

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


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


这是对Virgo47答案的扩展(见上文)。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/* 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 +")";
    }
}


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

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

它既适用于我的MacBook,也适用于我的Android手机

我也试过:

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

但Android会返回"getstacktrace"我可以用

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

但是我在我的笔记本上得到了错误的答案


java实用程序:

1
2
3
4
5
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:Java语言:

1
2
3
4
5
public class SomeClass {
    public static void main(String[] args) {
        System.out.println(Util.getCurrentClassAndMethodNames()); // output: SomeClass.main
    }
}


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


这可以从使用Java 9以来使用EDCOX1 0来完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
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。


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

从JavaLab.GETStAccTrace.()(这至少在Java 5之后是相同的):

The zeroth element of the array (assuming the array's length is non-zero) represents the top of the stack, which is the last method invocation in the sequence. Typically, this is the point at which this throwable was created and thrown.

下面的代码段假定类是非静态的(因为getClass()),但这是一个旁白。

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


我有使用这个的解决方案(在Android中)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * @param className       fully qualified className
 *                        <br/>
 *                        <wyn>YourClassName.class.getName();</wyn>
 *                        <br/><br/>
 * @param classSimpleName simpleClassName
 *                        <br/>
 *                        <wyn>YourClassName.class.getSimpleName();</wyn>
 *                        <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())}));
    }
}


我不知道获取当前执行的方法名称背后的意图是什么,但是如果这只是为了调试,那么像"logback"这样的日志框架可以在这里有所帮助。例如,在logback中,您所需要做的就是在日志配置中使用模式"%m"。但是,应小心使用,因为这可能会降低性能。


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


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


这种方法有什么问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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(...)之前,您可以并且应该创建一些方法,以便可以重定向输出,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    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());
        }
    }