Чи є спосіб отримати ім'я виконуваного в даний час методу на Java?
Чи є спосіб отримати ім'я виконуваного в даний час методу на Java?
Відповіді:
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
.
getEnclosingMethod
отримує назву методу, де визначено клас. this.getClass()
зовсім не допоможе тобі. @wutzebaer навіщо вам це потрібно? Ви вже маєте доступ до них.
Січень 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 року:
синюваті коментарі:
Я використовую JRE 6 і дає мені неправильну назву методу.
Це працює, якщо я пишуste[2 + depth].getMethodName().
0
єgetStackTrace()
,1
єgetMethodName(int depth)
і2
є методом виклику.
virgo47 «s відповідь (upvoted) фактично обчислює вказівний застосовувати для того , щоб отримати назад назву методу.
StackTraceElement
масив для налагодження і переконатися, що "main" насправді правильний метод?
Цей код ми використовували для зменшення потенційної мінливості в індексі відстеження стека - тепер просто виклик 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();
}
}
ім'я матиме значення 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) безпосередньо. І зберігає доступний метод у статичній змінній.
new Throwable().getStackTrace()
5614 мс.
Використовуйте наступний код:
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
* /programming/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. /programming/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 () називає цю
Це можна зробити за допомогою 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 для отримання додаткової інформації.
Альтернативний метод - створити, але не кидати, виняток, і використовувати той об'єкт, з якого можна отримати дані про стеження стека, оскільки метод укладання, як правило, буде в індексі 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);
У мене є рішення, використовуючи це (В 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". Наприклад, для зворотного зв'язку, все, що вам потрібно зробити, - це використовувати шаблон "% M" у своїй конфігурації реєстрації . Однак це слід використовувати обережно, оскільки це може погіршити продуктивність.
На випадок, якщо метод, який ви хочете знати, є методом тестування Джуніт, тоді ви можете використовувати правило junit TestName: https://stackoverflow.com/a/1426730/3076107
Більшість відповідей тут здається неправильним.
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
Я трохи переписав відповідь маклеменса :
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);
}
MethodHandles.lookup().lookupClass().getEnclosingMethod().getName();
getEnclosingMethod()
кидає NullPointerException
для мене на Java 7.
Що не так у цьому підході:
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());
}
}