首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Java 獲取當前方法名與類名的兩種方法

     編輯:Java 程式設計入門

問題背景

C/C++ 可用 __func__ 或 __FUNCTION__ 直接獲取函數名。Java 中一切皆物件,需從堆疊追蹤物件獲取。

方法一:Thread.currentThread().getStackTrace()


public class MethodNameDemo {
    public static void main(String[] args) {
        printMethodName();
    }
    
    static void printMethodName() {
        StackTraceElement[] stack = Thread.currentThread().getStackTrace();
        // stack[0] = getStackTrace()
        // stack[1] = printMethodName()
        // stack[2] = main()
        StackTraceElement caller = stack[2];  // 視調用深度調整索引
        
        System.out.println("方法名: " + caller.getMethodName());
        System.out.println("類名: " + caller.getClassName());
        System.out.println("檔案: " + caller.getFileName());
        System.out.println("行號: " + caller.getLineNumber());
    }
}

輸出


方法名: main
類名: MethodNameDemo
檔案: MethodNameDemo.java
行號: 10

方法二:new Throwable().getStackTrace()


public class MethodNameDemo2 {
    public static void main(String[] args) {
        printMethodName();
    }
    
    static void printMethodName() {
        StackTraceElement[] stack = new Throwable().getStackTrace();
        // stack[0] = printMethodName()
        // stack[1] = main()
        StackTraceElement current = stack[0];
        StackTraceElement caller = stack[1];
        
        System.out.println("當前方法: " + current.getMethodName());
        System.out.println("調用者方法: " + caller.getMethodName());
        System.out.println("調用者類名: " + caller.getClassName());
    }
}

輸出


當前方法: printMethodName
調用者方法: main
調用者類名: MethodNameDemo2

關鍵差異:索引位置

方法stack[0]stack[1]適用場景
Thread.getStackTrace()getStackTrace() 呼叫點當前方法需跳過 1-2 層獲取調用者
new Throwable()當前方法調用者方法直接獲取當前/調用者

索引不固定的原因

  • JDK 版本差異
  • JIT 編譯器內聯優化可能移除堆疊幀
  • Lambda/方法引用產生合成方法
  • 異步執行緒堆疊不同

健壯的工具方法


public class StackTraceUtil {
    
    /** 獲取調用者資訊(跳過工具方法本身) */
    public static StackTraceElement getCaller(int skipFrames) {
        StackTraceElement[] stack = new Throwable().getStackTrace();
        int index = 1 + skipFrames;  // stack[0]=getCaller, stack[1]=直接調用者
        if (index < stack.length) return stack[index];
        return null;
    }
    
    /** 獲取當前方法名 */
    public static String getCurrentMethodName() {
        return new Throwable().getStackTrace()[0].getMethodName();
    }
    
    /** 獲取調用者方法名 */
    public static String getCallerMethodName() {
        return new Throwable().getStackTrace()[1].getMethodName();
    }
    
    /** 獲取調用者類名 */
    public static String getCallerClassName() {
        return new Throwable().getStackTrace()[1].getClassName();
    }
    
    /** 列印完整堆疊(除錯用) */
    public static void printStackTrace() {
        for (StackTraceElement e : new Throwable().getStackTrace()) {
            System.out.println(e);
        }
    }
}

// 使用範例
class Service {
    public void doBusiness() {
        logEntry();
        // 業務邏輯...
        logExit();
    }
    
    void logEntry() {
        StackTraceElement caller = StackTraceUtil.getCaller(1);  // 跳過 logEntry
        System.out.printf("[ENTER] %s.%s()%n", caller.getClassName(), caller.getMethodName());
    }
    
    void logExit() {
        StackTraceElement caller = StackTraceUtil.getCaller(1);
        System.out.printf("[EXIT] %s.%s()%n", caller.getClassName(), caller.getMethodName());
    }
}

效能考量

  • 建立 Throwable 物件較重:涉及堆疊快照捕獲,頻繁呼叫影響效能
  • 避免在熱路徑使用:高頻迴圈、遞迴、即時系統
  • 替代方案:

// 編譯期常數(Java 9+ StackWalker API)
private static final StackWalker WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);

public static String getCurrentMethodNameFast() {
    return WALKER.getCallerClass().getSimpleName();  // 需配合 getMethodName 邏輯
}

// 或使用 AOP/編譯期插桩(如 Log4j2、Spring AOP)

Java 9+ StackWalker API(推薦現代專案)


import java.lang.StackWalker.StackFrame;

public class ModernStackTrace {
    private static final StackWalker WALKER = StackWalker.getInstance(
        StackWalker.Option.RETAIN_CLASS_REFERENCE
    );
    
    public static void demo() {
        WALKER.walk(frames -> frames
            .skip(1)  // 跳過 walk 呼叫
            .findFirst()
            .ifPresent(frame -> {
                System.out.println("類名: " + frame.getClassName());
                System.out.println("方法名: " + frame.getMethodName());
                System.out.println("行號: " + frame.getLineNumber());
            }));
    }
}

應用場景

結語

Java 獲取方法名依賴堆疊追蹤,new Throwable().getStackTrace()[0] 取當前方法,[1] 取調用者;Thread.getStackTrace() 索引偏移 1。索引非固定,建議封裝工具類並測試驗證。現代專案(Java 9+)優先使用 StackWalker API,效能更好且 API 更豐富。高頻場景避免直接使用,改用 AOP 或編譯期方案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
場景用途注意
日誌框架自動記錄呼叫位置使用 StackWalker 或編譯期插桩
除錯輔助印出呼叫鏈路開發階段可接受效能開銷
安全審計記錄敏感操作發起者確保堆疊不可竄改
測試框架標記測試方法來源JUnit 內建支援