Получение имени текущего выполняемого метода
Есть ли способ получить имя выполняющегося в настоящее время метода в Java?
24 ответа
Thread
, currentThread()
, getStackTrace()
обычно содержит метод, из которого вы его вызываете, но есть подводные камни (см. Javadoc):
Некоторые виртуальные машины могут при некоторых обстоятельствах пропускать один или несколько стековых фреймов из трассировки стека. В крайнем случае виртуальной машине, у которой нет информации трассировки стека относительно этого потока, разрешено возвращать массив нулевой длины из этого метода.
Технически это будет работать...
String name = new Object(){}.getClass().getEnclosingMethod().getName();
Тем не менее, новый анонимный внутренний класс будет создан во время компиляции (например, YourClass$1.class
). Так что это создаст .class
файл для каждого метода, который использует этот трюк. Кроме того, экземпляр неиспользуемого объекта создается при каждом вызове во время выполнения. Так что это может быть приемлемой уловкой отладки, но она сопряжена со значительными накладными расходами.
Преимущество этого трюка в том, что getEncosingMethod()
возвращается java.lang.reflect.Method
которая может использоваться для получения всей другой информации о методе, включая аннотации и имена параметров. Это позволяет различать конкретные методы с одинаковыми именами (перегрузка метода).
Обратите внимание, что в соответствии с JavaDoc getEnclosingMethod()
этот трюк не должен бросать SecurityException
поскольку внутренние классы должны загружаться с использованием того же загрузчика классов. Поэтому нет необходимости проверять условия доступа, даже если присутствует менеджер безопасности.
Требуется использовать getEnclosingConstructor()
для конструкторов. Во время блоков вне (именованных) методов, getEnclosingMethod()
возвращается null
,
Январь 2009 г.:
Полный код будет (для использования с учетом предостережения @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 года:
bluish комментарии:
Я использую JRE 6 и дает мне неправильное имя метода.
Работает если я напишуste[2 + depth].getMethodName().
0
являетсяgetStackTrace()
,1
являетсяgetMethodName(int depth)
а также2
вызывает метод.
Ответ virgo47 (upvoted) на самом деле вычисляет правильный индекс для применения, чтобы вернуть имя метода.
Мы использовали этот код для смягчения потенциальной изменчивости в индексе трассировки стека - теперь просто вызовите 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 не делает это плохо.:-)
Оба эти варианта работают для меня с Java:
new Object(){}.getClass().getEnclosingMethod().getName()
Или же:
Thread.currentThread().getStackTrace()[1].getMethodName()
public class SomeClass {
public void foo(){
class Local {};
String name = Local.class.getEnclosingMethod().getName();
}
}
имя будет иметь значение foo.
Это можно сделать с помощью 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();
}
StackWalker
разработан, чтобы быть ленивым, поэтому он, вероятно, будет более эффективным, чем, скажем, Thread.getStackTrace
который охотно создает массив для всего стека вызовов. Также см. JEP для получения дополнительной информации.
Самый быстрый способ, который я нашел, заключается в том, что:
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 глубине). И сохраняет доступный Метод в статической переменной.
Используйте следующий код:
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
StackTraceElement e = stacktrace[1];//coz 0th will be getStackTrace so 1st
String methodName = e.getMethodName();
System.out.println(methodName);
public static String getCurrentMethodName() {
return Thread.currentThread().getStackTrace()[2].getClassName() + "." + Thread.currentThread().getStackTrace()[2].getMethodName();
}
Это расширение ответа virgo47 (см. Выше).
Он предоставляет несколько статических методов для получения текущих и вызывающих имен классов / методов.
/* Utility class: Getting the name of the current executing method
* https://stackru.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 stackru posts eg. https://stackru.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.stackru.util;
public class StackTraceInfo
{
/* (Lifted from virgo47's stackru 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
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
}
}
Альтернативный метод заключается в создании, но не создании исключения, и использовании этого объекта, из которого можно получить данные трассировки стека, поскольку метод включения обычно будет иметь индекс 0 - пока JVM хранит эту информацию, как это делают другие. упомянутое выше. Однако это не самый дешевый метод.
Из Throwable.getStackTrace () (это было так же, как минимум с Java 5):
Нулевой элемент массива (при условии, что длина массива не равна нулю) представляет вершину стека, которая является последним вызовом метода в последовательности. Как правило, это точка, в которой этот бросаемый объект был создан и брошен.
В приведенном ниже фрагменте предполагается, что класс не является статичным (из-за 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);
На тот случай, если метод, имя которого вы хотите узнать, является тестовым методом junit, вы можете использовать правило TestName junit: /questions/27549980/poluchit-imya-vyipolnyaemogo-v-dannyij-moment-testa-v-junit-4/27550019#27550019
Я не знаю, каково намерение получить имя исполняемого в данный момент метода, но если это только для целей отладки, то здесь могут помочь каркасы журналирования, такие как "logback". Например, при входе в систему все, что вам нужно сделать, это использовать шаблон "%M" в вашей конфигурации ведения журнала. Однако это следует использовать с осторожностью, поскольку это может ухудшить производительность.
Я использую этот фрагмент кода с последней версией Android Studio с последним обновлением Java. Его можно вызвать из любого 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)]
У меня есть решение с помощью этого (в 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())}));
}
}
Предпочтительное решение для Java 9 или выше:
private static String getCurrentMethodName() {
return StackWalker.getInstance()
.walk(frames -> frames.skip(1).findFirst().map(StackWalker.StackFrame::getMethodName))
.orElse("Anything");
}
StackWalker
имеет ряд преимуществ:
- нет необходимости создавать фиктивный анонимный экземпляр внутреннего класса, т.е.
new Object().getClass() {}
- нет необходимости быстро захватывать всю трассировку стека, что может быть дорогостоящим
Подробнее читайте в этой статье .
Большинство ответов здесь кажется неправильным.
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
MethodHandles.lookup().lookupClass().getEnclosingMethod().getName();
Я немного переписал ответ Маклеменца:
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);
}
Что не так с этим подходом:
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());
}
}