【发布时间】:2010-10-01 08:40:18
【问题描述】:
有没有办法在 Java 中获取当前正在执行的方法的名称?
【问题讨论】:
标签: java reflection methods
有没有办法在 Java 中获取当前正在执行的方法的名称?
【问题讨论】:
标签: java reflection methods
从技术上讲,这会起作用...
String name = new Object(){}.getClass().getEnclosingMethod().getName();
但是,在编译期间将创建一个新的匿名内部类(例如YourClass$1.class)。因此,这将为部署此技巧的每个方法创建一个.class 文件。此外,在运行时每次调用时都会创建一个未使用的对象实例。所以这可能是一个可以接受的调试技巧,但它确实会带来很大的开销。
此技巧的一个优点是getEnclosingMethod() 返回java.lang.reflect.Method,可用于检索方法的所有其他信息,包括注释和参数名称。这使得区分具有相同名称的特定方法成为可能(方法重载)。
请注意,根据getEnclosingMethod() 的JavaDoc,此技巧不应抛出SecurityException,因为应使用相同的类加载器加载内部类。因此,即使有安全管理员,也无需检查访问条件。
请注意:构造函数必须使用getEnclosingConstructor()。在(命名的)方法之外的块期间,getEnclosingMethod() 返回null。
【讨论】:
getEnclosingMethod 获取定义类的方法的名称。 this.getClass() 根本不会帮助你。 @wutzebaer 为什么你甚至需要?您已经可以访问它们了。
@987654321@.@987654322@.@987654323@ 通常会包含您从中调用它的方法,但存在缺陷(请参阅Javadoc):
在某些情况下,某些虚拟机可能会从堆栈跟踪中省略一个或多个堆栈帧。在极端情况下,允许没有关于该线程的堆栈跟踪信息的虚拟机从该方法返回一个长度为零的数组。
【讨论】:
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 月更新:
bluishcmets:
我使用 JRE 6 并给了我错误的方法名称。
如果我写ste[2 + depth].getMethodName().就可以了
0是getStackTrace(),1是getMethodName(int depth)和2正在调用方法。
【讨论】:
StackTraceElement 数组以进行调试并查看“main”是否真的是正确的方法?
ste[2 + depth].getMethodName(),它会起作用。 0 是getStackTrace(),1 是getMethodName(int depth),2 是调用方法。另见@virgo47's answer。
我们使用此代码来减轻堆栈跟踪索引的潜在可变性 - 现在只需调用 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 不会做得那么糟糕。 :-)
【讨论】:
public class SomeClass {
public void foo(){
class Local {};
String name = Local.class.getEnclosingMethod().getName();
}
}
name 的值为 foo。
【讨论】:
null
这两个选项都适用于 Java:
new Object(){}.getClass().getEnclosingMethod().getName()
或者:
Thread.currentThread().getStackTrace()[1].getMethodName()
【讨论】:
最快的方法我发现是:
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 存储在静态变量中。
【讨论】:
new Throwable().getStackTrace() 耗时 5614ms。
使用以下代码:
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
StackTraceElement e = stacktrace[1];//coz 0th will be getStackTrace so 1st
String methodName = e.getMethodName();
System.out.println(methodName);
【讨论】:
这可以从 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 更高效,后者急切地为整个调用堆栈创建一个数组。 Also see the JEP for more information.
【讨论】:
public static String getCurrentMethodName() {
return Thread.currentThread().getStackTrace()[2].getClassName() + "." + Thread.currentThread().getStackTrace()[2].getMethodName();
}
【讨论】:
这是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 + ")";
}
}
【讨论】:
要获取调用当前方法的方法的名称,您可以使用:
new Exception("is not thrown").getStackTrace()[1].getMethodName()
这适用于我的 MacBook 和我的 Android 手机
我也试过了:
Thread.currentThread().getStackTrace()[1]
但 Android 将返回“getStackTrace” 我可以为 Android 修复这个问题
Thread.currentThread().getStackTrace()[2]
但后来我在我的 MacBook 上得到了错误的答案
【讨论】:
getStackTrace()[0] 比使用getStackTrace()[1] 效果更好。 YMMV。
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
}
}
【讨论】:
final StackTraceElement e = Thread.currentThread().getStackTrace()[2]; 工作; e.getClassName(); 返回完整的类名,e.getMethodName() 返回方法名。
getStackTrace()[2] 是错误的,它必须是getStackTrace()[3] 因为: [0] dalvik.system.VMStack.getThreadStackTrace [1] java.lang.Thread.getStackTrace [2] Utils.getCurrentClassAndMethodNames [ 3]调用这个的函数a()
另一种方法是创建但不抛出异常,并使用该对象从中获取堆栈跟踪数据,因为封闭方法通常位于索引 0 - 只要 JVM 存储该信息,正如其他人在上面提到的那样。然而,这并不是最便宜的方法。
来自Throwable.getStackTrace()(至少从 Java 5 开始就是这样):
数组的第零个元素(假设数组的长度不为零)代表栈顶,也就是序列中的最后一个方法调用。 通常,这是创建和抛出此可抛物的点。
下面的 sn-p 假设类是非静态的(因为 getClass()),但这是一个旁白。
System.out.printf("Class %s.%s\n", getClass().getName(), new Exception("is not thrown").getStackTrace()[0].getMethodName());
【讨论】:
String methodName =Thread.currentThread().getStackTrace()[1].getMethodName();
System.out.println("methodName = " + methodName);
【讨论】:
我有使用这个的解决方案(在 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())}));
}
}
【讨论】:
我不知道获取当前执行的方法名称的目的是什么,但如果这只是为了调试目的,那么像“logback”这样的日志记录框架可以在这里提供帮助。例如,在 logback 中,您需要做的就是use the pattern "%M" in your logging configuration。但是,应谨慎使用,因为这可能会降低性能。
【讨论】:
万一你想知道名字的方法是junit测试方法,那么你可以使用junit TestName规则:https://stackoverflow.com/a/1426730/3076107
【讨论】:
我改写了一点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);
}
【讨论】:
这里的大多数答案似乎都是错误的。
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
【讨论】:
我将此代码 sn-p 与带有最新 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)]
【讨论】:
MethodHandles.lookup().lookupClass().getEnclosingMethod().getName();
【讨论】:
getEnclosingMethod() 在 Java 7 中为我抛出 NullPointerException。
这种方法有什么问题:
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());
}
}
【讨论】: