C语言中有__FILE__、__LINE__等预定义宏,用于获取当前文件名和行号等信息,而且它们的值在预处理时就已经确定了,不会占用运行时时间去计算,这对打印日志相当有用。那么,Java语言是否也有类似的功能呢?
Java是否提供某种方法:可以让用户代码在编译时确定源码行号等信息,本人暂时不知晓。不过从网上搜索得到的方法大致是:
- Thread.currentThread().getStackTrace()[1].getFileName():获取当前文件名;
- Thread.currentThread().getStackTrace()[1].getLineNumber():获取当前行号。
其中:Thread.currentThread().getStackTrace()返回的是一个数组形式的函数调用栈(栈顶在索引0处),其中第1个元素(索引为0)为最新调用的函数信息(getStackTrace()),第2个元素(索引为1)为当前函数(即调用getStackTrace()的函数)信息。示例:
1 public class Test { 2 public static void main(String args[]) { 3 StackTraceElement[] stack = Thread.currentThread().getStackTrace(); 4 for (int i = 0; i < stack.length; ++i) 5 System.out.println(stack[i].getFileName() + ":" + stack[i].getLineNumber() + ": " + stack[i].getMethodName()); 6 System.out.println(""); 7 foo(); 8 } 9 static void foo() { 10 StackTraceElement[] stack = Thread.currentThread().getStackTrace(); 11 for (int i = 0; i < stack.length; ++i) 12 System.out.println(stack[i].getFileName() + ":" + stack[i].getLineNumber() + ": " + stack[i].getMethodName()); 13 System.out.println(""); 14 bar(); 15 } 16 static void bar() { 17 StackTraceElement[] stack = Thread.currentThread().getStackTrace(); 18 for (int i = 0; i < stack.length; ++i) 19 System.out.println(stack[i].getFileName() + ":" + stack[i].getLineNumber() + ": " + stack[i].getMethodName()); 20 } 21 }
输出:
$ javac Test.java $ java Test null:-1: getStackTrace Test.java:3: main null:-1: getStackTrace Test.java:10: foo Test.java:7: main null:-1: getStackTrace Test.java:17: bar Test.java:14: foo Test.java:7: main
于是,如果我们想在日志中打印当前文件名和行号,就可以:
1 public class Test { 2 public static void main(String args[]) { 3 System.out.println(Thread.currentThread().getStackTrace()[1].getFileName() + ":" + Thread.currentThread().getStackTrace()[1].getLineNumber()); 4 foo(); 5 } 6 static void foo() { 7 System.out.println(Thread.currentThread().getStackTrace()[1].getFileName() + ":" + Thread.currentThread().getStackTrace()[1].getLineNumber()); 8 bar(); 9 } 10 static void bar() { 11 System.out.println(Thread.currentThread().getStackTrace()[1].getFileName() + ":" + Thread.currentThread().getStackTrace()[1].getLineNumber()); 12 } 13 }
输出:
$ javac Test.java $ java Test Test.java:3 Test.java:7 Test.java:11
不过每次都要写这么长的几串代码“Thread.currentThread().getStackTrace()[1].getXXX()”拼一起看起来难看敲起来也累。于是我们可以将getStackTrace()封装到另一个函数中:
1 public class Test { 2 public static void main(String args[]) { 3 System.out.println(__FILE__() + ":" + __LINE__()); 4 foo(); 5 } 6 static void foo() { 7 System.out.println(__FILE__() + ":" + __LINE__()); 8 bar(); 9 } 10 static void bar() { 11 System.out.println(__FILE__() + ":" + __LINE__()); 12 } 13 static String __FILE__() { 14 return Thread.currentThread().getStackTrace()[2].getFileName(); 15 } 16 static int __LINE__() { 17 return Thread.currentThread().getStackTrace()[2].getLineNumber(); 18 } 19 }
输出结果一样:
$ javac Test.java $ java Test Test.java:3 Test.java:7 Test.java:11
参考:
- http://*.com/questions/22677815/eclipse-println-line-does-such-a-preprocessor-macro-exist-to-program
- http://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#getStackTrace--
- http://docs.oracle.com/javase/8/docs/api/java/lang/StackTraceElement.html
- http://*.com/questions/115008/how-can-we-print-line-numbers-to-the-log-in-java
- http://www.codeceo.com/article/8-java-log-framework.html