背景:C/C++ 的 __FILE__、__LINE__ 宏
// C 範例
#include
int main() {
fprintf(stdout, "[%s:%d] Hello World!", __FILE__, __LINE__);
return 0;
}
// 輸出: [main.c:4] Hello World!
Java 無預處理器宏,但提供 java.lang.StackTraceElement 類在運行時獲取堆棧幀資訊。
兩種獲取途徑
| 途徑 | 代碼示例 | 適用場景 |
Thread.currentThread().getStackTrace() | 當前線程完整堆棧快照 | 通用工具方法、日誌框架 |
new Throwable().getStackTrace() | 拋出點堆棧(不實際拋出) | 輕量級,避免創建完整線程堆棧 |
封裝工具類
public class SourceLocation {
// 堆棧索引說明:
// [0] = getStackTrace() 呼叫點
// [1] = getLineNumber()/getFileName() 所在方法
// [2] = 呼叫此工具方法的「調用者」 ← 我們想要的
private static final int CALLER_DEPTH = 2;
public static int getLineNumber() {
return Thread.currentThread().getStackTrace()[CALLER_DEPTH].getLineNumber();
}
public static String getFileName() {
return Thread.currentThread().getStackTrace()[CALLER_DEPTH].getFileName();
}
public static String getMethodName() {
return Thread.currentThread().getStackTrace()[CALLER_DEPTH].getMethodName();
}
public static String getClassName() {
return Thread.currentThread().getStackTrace()[CALLER_DEPTH].getClassName();
}
// 格式化輸出範例
public static void printLocation(String msg) {
StackTraceElement ste = Thread.currentThread().getStackTrace()[CALLER_DEPTH];
System.out.printf("[%s:%d %s] %s%n",
ste.getFileName(), ste.getLineNumber(), ste.getMethodName(), msg);
}
}
測試驗證
public class LineNoTest {
public static void main(String[] args) {
SourceLocation.printLocation("Hello World!");
// 輸出範例: [LineNoTest.java:10 main] Hello World!
}
}
魔法數字 2 的含義
getStackTrace() 返回陣列索引對應:
- 0:
Thread.getStackTrace() 內部本機方法調用點。
- 1:
SourceLocation.getLineNumber() 方法調用點。
- 2:呼叫
SourceLocation.getLineNumber() 的那一行代碼(即業務調用處)。
若工具方法再被封裝一層,深度需相應增加(如 3)。建議封裝為 getCaller(int depth) 靈活指定。
效能與替代方案
getStackTrace() 較重(捕獲完整堆棧),高頻日誌建議用 new Throwable().getStackTrace() 或 Java 9+ StackWalker API(延遲、可篩選、支援隱藏框架幀)。
- Log4j2、SLF4J 等成熟日誌框架已內建高效位置資訊獲取,業務代碼宜直接使用框架提供的
%F:%L 模式。
- 上一篇文章: J2ME 遊戲開發總結:架構心得、效能優化與遺留 Bug 剖析
- 下一篇文章: UML 類圖設計 Java 應用實戰(二):CGObject 子類實現與主程式架構
Copyright ©
程式碼之家 All Rights Reserved