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

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

448124 次浏览

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

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

2009年1月< p >: < br > 完整的代码将是(记住@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回答 (upvotes)实际上计算要应用的正确索引,以获得方法名。

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

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

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

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

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

请注意:必须为构造函数使用getEnclosingConstructor()。在(命名)方法之外的块中,getEnclosingMethod()返回null

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

使用以下代码:

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

最快的方法我发现是:

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

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

String methodName =Thread.currentThread().getStackTrace()[1].getMethodName();
System.out.println("methodName = " + methodName);
public static String getCurrentMethodName() {
return Thread.currentThread().getStackTrace()[2].getClassName() + "." + Thread.currentThread().getStackTrace()[2].getMethodName();
}

这种方法的错误之处在于:

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

另一种方法是创建(但不是抛出)一个Exception,并使用该对象从中获取堆栈跟踪数据,因为封闭方法将通常< em > < / em >位于索引0——只要JVM存储该信息,就像上面其他人提到的那样。然而,这并不是最便宜的方法。

来自Throwable.getStackTrace ()(至少自Java 5以来一直如此):

数组的第0个元素(假设数组的长度非零)表示堆栈的顶部,这是序列中的最后一个方法调用。通常< em > < / em >,这是这个可抛出对象被创建和抛出的点。

下面的代码片段假设该类是非静态的(因为getClass()),但这只是题外话。

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

这两个选项都适合我使用Java:

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

或者:

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

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

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

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

我还试过:

Thread.currentThread().getStackTrace()[1]
但是Android会返回"getStackTrace" 我可以修复这个Android与

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

但后来我在笔记本电脑上得到了错误的答案

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

我有解决方案使用这个(在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())}));
}
}
MethodHandles.lookup().lookupClass().getEnclosingMethod().getName();

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

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

这可以从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更有效,后者会为整个调用堆栈急切地创建一个数组。更多信息请参见JEP。

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


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

    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

我使用这个代码片段与最新的Android Studio与最新的Java更新。它可以从任何活动、片段等调用。

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