Có cách nào để lấy tên của phương thức hiện đang thực thi trong Java không?
Có cách nào để lấy tên của phương thức hiện đang thực thi trong Java không?
Câu trả lời:
Thread.currentThread().getStackTrace()
thường sẽ chứa phương thức bạn gọi nó từ nhưng có những cạm bẫy (xem Javadoc ):
Một số máy ảo có thể, trong một số trường hợp, bỏ qua một hoặc nhiều khung ngăn xếp từ dấu vết ngăn xếp. Trong trường hợp cực đoan, một máy ảo không có thông tin theo dõi ngăn xếp liên quan đến luồng này được phép trả về một mảng có độ dài bằng không từ phương thức này.
Về mặt kỹ thuật, nó sẽ hoạt động ...
String name = new Object(){}.getClass().getEnclosingMethod().getName();
Tuy nhiên, một lớp bên trong ẩn danh mới sẽ được tạo trong thời gian biên dịch (ví dụ YourClass$1.class
). Vì vậy, điều này sẽ tạo ra một .class
tệp cho mỗi phương thức triển khai thủ thuật này. Ngoài ra, một đối tượng không sử dụng khác được tạo trên mỗi lần gọi trong thời gian chạy. Vì vậy, đây có thể là một mẹo gỡ lỗi chấp nhận được, nhưng nó đi kèm với chi phí đáng kể.
Một lợi thế của thủ thuật này là getEncosingMethod()
trả về java.lang.reflect.Method
có thể được sử dụng để lấy tất cả thông tin khác của phương thức bao gồm chú thích và tên tham số. Điều này cho phép phân biệt giữa các phương thức cụ thể có cùng tên (quá tải phương thức).
Lưu ý rằng theo JavaDoc của getEnclosingMethod()
thủ thuật này, không nên ném một SecurityException
lớp vì các lớp bên trong nên được tải bằng cùng một trình nạp lớp. Vì vậy, không cần phải kiểm tra các điều kiện truy cập ngay cả khi có trình quản lý bảo mật.
Nó là cần thiết để sử dụng getEnclosingConstructor()
cho các nhà xây dựng. Trong các khối bên ngoài các phương thức (được đặt tên), getEnclosingMethod()
trả về null
.
getEnclosingMethod
lấy tên của phương thức trong đó lớp được định nghĩa. this.getClass()
sẽ không giúp bạn chút nào @wutzebaer tại sao bạn thậm chí cần phải? Bạn đã truy cập chúng rồi.
Tháng 1 năm 2009:
Một mã đầy đủ sẽ là (để sử dụng với suy nghĩ của @ 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
}
Thêm trong câu hỏi này .
Cập nhật tháng 12 năm 2011:
ý kiến hơi xanh :
Tôi sử dụng JRE 6 và cung cấp cho tôi tên phương thức không chính xác.
Nó hoạt động nếu tôi viếtste[2 + depth].getMethodName().
0
làgetStackTrace()
,1
làgetMethodName(int depth)
và2
đang gọi phương thức.
Câu trả lời của virgo47 (được nâng cấp) thực sự tính toán chỉ số đúng để áp dụng để lấy lại tên phương thức.
StackTraceElement
mảng cho mục đích gỡ lỗi và để xem liệu 'chính' có thực sự là phương pháp đúng không?
ste[2 + depth].getMethodName()
. 0 là getStackTrace()
, 1 là getMethodName(int depth)
và 2 là gọi phương thức. Xem thêm câu trả lời của @ virgo47 .
Chúng tôi đã sử dụng mã này để giảm thiểu khả năng thay đổi tiềm năng trong chỉ mục theo dõi ngăn xếp - bây giờ chỉ cần gọi phương thứcName tận dụng:
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();
}
}
Có vẻ bị áp đảo, nhưng chúng tôi đã có một số số cố định cho JDK 1.5 và hơi ngạc nhiên khi nó thay đổi khi chúng tôi chuyển sang JDK 1.6. Bây giờ nó giống với Java 6/7, nhưng bạn không bao giờ biết. Nó không phải là bằng chứng cho những thay đổi trong chỉ mục đó trong thời gian chạy - nhưng hy vọng HotSpot không làm điều đó tồi tệ. :-)
public class SomeClass {
public void foo(){
class Local {};
String name = Local.class.getEnclosingMethod().getName();
}
}
Tên sẽ có giá trị foo.
null
Cả hai tùy chọn này đều hoạt động với tôi với Java:
new Object(){}.getClass().getEnclosingMethod().getName()
Hoặc là:
Thread.currentThread().getStackTrace()[1].getMethodName()
Cách nhanh nhất tôi tìm thấy là:
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;
}
}
}
Nó truy cập trực tiếp vào phương thức gốc getStackTraceEuity (int height). Và lưu trữ Phương thức có thể truy cập trong một biến tĩnh.
new Throwable().getStackTrace()
mất 5614ms.
Sử dụng mã sau đây:
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();
}
Đây là một bản mở rộng về câu trả lời của virgo47 (ở trên).
Nó cung cấp một số phương thức tĩnh để có được các tên lớp / phương thức hiện tại và gọi.
/* 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 + ")";
}
}
Để có được tên của phương thức được gọi là phương thức hiện tại, bạn có thể sử dụng:
new Exception("is not thrown").getStackTrace()[1].getMethodName()
Điều này hoạt động trên MacBook cũng như trên điện thoại Android của tôi
Tôi cũng đã thử:
Thread.currentThread().getStackTrace()[1]
nhưng Android sẽ trả về "getStackTrace" Tôi có thể sửa lỗi này cho Android bằng
Thread.currentThread().getStackTrace()[2]
nhưng sau đó tôi nhận được câu trả lời sai trên MacBook của tôi
getStackTrace()[0]
hơn là 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();
}
Một sốClass.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];
làm; e.getClassName();
trả lại tên lớp đầy đủ và e.getMethodName()
trả lại tên methon.
getStackTrace()[2]
là sai, nó phải được getStackTrace()[3]
bởi vì: [0] dalvik.system.VMStack.getThreadStackTrace [1] java.lang.Thread.getStackTrace [2] Utils.getCurrentClassAndMethodNames [3] Các chức năng một () gọi này
Điều này có thể được thực hiện bằng cách sử dụng 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
được thiết kế để lười biếng, do đó, nó có khả năng hiệu quả hơn, giả sử, Thread.getStackTrace
háo hức tạo ra một mảng cho toàn bộ cửa hàng. Cũng xem JEP để biết thêm thông tin.
Một phương thức thay thế là tạo, nhưng không ném, Ngoại lệ và sử dụng đối tượng đó để lấy dữ liệu theo dõi ngăn xếp, vì phương thức kèm theo thường sẽ ở chỉ số 0 - miễn là JVM lưu trữ thông tin đó, như những người khác có đã đề cập ở trên. Đây không phải là phương pháp rẻ nhất, tuy nhiên.
Từ throwable.getStackTrace () (điều này ít nhất giống với Java 5):
Phần tử zeroth của mảng (giả sử độ dài của mảng là khác không) đại diện cho đỉnh của ngăn xếp, đây là lời gọi phương thức cuối cùng trong chuỗi. Thông thường , đây là điểm mà cái ném này được tạo ra và ném.
Đoạn mã dưới đây giả định lớp không tĩnh (vì getClass ()), nhưng đó là một bên.
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);
Tôi đã có giải pháp sử dụng điều này (Trong 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())}));
}
}
Tôi không biết ý định đằng sau việc lấy tên của phương thức hiện được thực hiện là gì, nhưng nếu đó chỉ nhằm mục đích gỡ lỗi, thì các khung đăng nhập như "logback" có thể giúp ích ở đây. Ví dụ, trong logback, tất cả những gì bạn cần làm là sử dụng mẫu "% M" trong cấu hình ghi nhật ký của bạn . Tuy nhiên, điều này nên được sử dụng một cách thận trọng vì điều này có thể làm giảm hiệu suất.
Chỉ trong trường hợp phương thức mà tên bạn muốn biết là phương thức kiểm tra Junit, thì bạn có thể sử dụng quy tắc Testit của Junit: https://stackoverflow.com/a/1426730/3076107
Hầu hết các câu trả lời ở đây có vẻ sai.
public static String getCurrentMethod() {
return getCurrentMethod(1);
}
public static String getCurrentMethod(int skip) {
return Thread.currentThread().getStackTrace()[1 + 1 + skip].getMethodName();
}
Thí dụ:
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) );
}
Đầu ra:
aaa -> aaa
aaa -> aaa
main -> main
Tôi đã viết lại một chút câu trả lời của 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);
}
MethodHandles.lookup().lookupClass().getEnclosingMethod().getName();
getEnclosingMethod()
ném NullPointerException
cho tôi trong Java 7.
Có gì sai với cách tiếp cận này:
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);
}
}
Và trước khi mọi người phát điên về việc sử dụng System.out.println(...)
bạn luôn có thể, và nên, tạo một số phương thức để đầu ra có thể được chuyển hướng, ví dụ:
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());
}
}