มีวิธีรับชื่อของวิธีการดำเนินการปัจจุบันใน 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
}
เพิ่มเติมในคำถามนี้
อัปเดตธันวาคม 2554:
ความคิดเห็นสีน้ำเงิน :
ฉันใช้ JRE 6 และทำให้ชื่อวิธีไม่ถูกต้อง
มันทำงานได้ถ้าฉันเขียนste[2 + depth].getMethodName().
0
คือgetStackTrace()
,1
เป็นgetMethodName(int depth)
และ2
เป็นวิธีการเรียก
คำตอบของvirgo47 (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();
}
}
ดูเหมือนว่า overengineered แต่เรามีหมายเลขคงที่สำหรับ 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()
เอา 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);
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" สามารถช่วยได้ที่นี่ ยกตัวอย่างเช่นใน logback ทั้งหมดที่คุณต้องทำคือการใช้รูปแบบ "M%" ในการกำหนดค่าการเข้าสู่ระบบของคุณ อย่างไรก็ตามควรใช้ด้วยความระมัดระวังเนื่องจากอาจทำให้ประสิทธิภาพลดลง
ในกรณีที่วิธีที่ชื่อที่คุณต้องการทราบนั้นเป็นวิธีทดสอบ junit จากนั้นคุณสามารถใช้กฎ 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
ฉันเขียนคำตอบของ 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()
โยน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());
}
}