得到当前堆栈信息的两种方式(Thread和Throwable)的方法

本文通过几个测试案例详细解析了Java中使用不同方法获取堆栈跟踪的区别,特别是Thread.currentThread().getStackTrace()与new Exception().getStackTrace()之间的差异,并给出了如何准确获取调用者类名的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天看到有一个工具类中有一句

Thread.currentThread().getStackTrace()[2].getClassName();


原来工作中遇到的问题:使用Thread.currentThread().getStackTrace()[1].getClassName()得到的是当前类而不是调用类。

所以弄个明白 。

我进行了一组测试:

测试A:

public class ThreadTest {

	public static void TestString(){
		StackTraceElement[] arr = new Exception().getStackTrace();
		for(int i=0;i<=arr.length-1;i++){
			System.out.println(arr[i].getClassName()+";"+arr[i].getMethodName()+";"+arr[i].getFileName());
		}
	}
}
public class App 
{
    public static void main( String[] args )
    {
        ThreadTest.TestString();
    }
}

     结果是:

test.ThreadTest;TestString;ThreadTest.java(当前方法和类)

test.App;main;App.java(调用该方法的方法和类)

测试B:

public class ThreadTest {

	public static void TestString(){
		StackTraceElement[] arr = Thread.currentThread().getStackTrace();
		for(int i=0;i<=arr.length-1;i++){
			System.out.println(arr[i].getClassName()+";"+arr[i].getMethodName()+";"+arr[i].getFileName());
		}
	}
}

App类同上,得到的结果是:

java.lang.Thread;getStackTrace;Thread.java(Thread的信息)

test.ThreadTest;TestString;ThreadTest.java(当前方法和类)

test.App;main;App.java(调用该方法的方法和类)

查看了下jdk的源码。

首先是Throwable的getStackTrace方法,代码如下:

public StackTraceElement[] getStackTrace() {
		return getOurStackTrace().clone();
	}

	private synchronized StackTraceElement[] getOurStackTrace() {
		// Initialize stack trace field with information from
		// backtrace if this is the first call to this method
		if (stackTrace == UNASSIGNED_STACK ||
			(stackTrace == null && backtrace != null) /* Out of protocol state */) {
			int depth = getStackTraceDepth();
			stackTrace = new StackTraceElement[depth];
			for (int i=0; i < depth; i++)
				stackTrace[i] = getStackTraceElement(i);
		} else if (stackTrace == null) {
			return UNASSIGNED_STACK;
		}
		return stackTrace;
	}

再看下Thread的getStackTrace方法,代码如下:

public StackTraceElement[] getStackTrace() {
		if (this != Thread.currentThread()) {
			// check for getStackTrace permission
			SecurityManager security = System.getSecurityManager();
			if (security != null) {
				security.checkPermission(
					SecurityConstants.GET_STACK_TRACE_PERMISSION);
			}
			// optimization so we do not call into the vm for threads that
			// have not yet started or have terminated
			if (!isAlive()) {
				return EMPTY_STACK_TRACE;
			}
			StackTraceElement[][] stackTraceArray = dumpThreads(new Thread[] {this});
			StackTraceElement[] stackTrace = stackTraceArray[0];
			// a thread that was alive during the previous isAlive call may have
			// since terminated, therefore not having a stacktrace.
			if (stackTrace == null) {
				stackTrace = EMPTY_STACK_TRACE;
			}
			return stackTrace;
		} else {
			// Don't need JVM help for current thread
			return (new Exception()).getStackTrace();
		}
	}

在if语句里面因为   this != Thread.currentThread()是为true的,所以方法会执行else里面的语句

return (new Exception()).getStackTrace();

new Exception().getStackTrace();就是这句话让使用Thread的getStackTrace方法就有可能多打印一句java.lang.Thread;getStackTrace;Thread.java      

这也就是为什么使用

logger = LoggerFactory.getLogger(Thread.currentThread().getStackTrace()[1].getClassName());

会得不到正确的日志信息的原因了!

应该就是这样子了,为了验证我想的对不对,写了一个测试验证的例子,代码如下:

public class TestException {

	public static StackTraceElement[] getStackTrace() {
		return new Exception().getStackTrace();
	}
}
public class ThreadTest {

	public static void TestString(){
		StackTraceElement[] arr = TestException.getStackTrace();
		for(int i=0;i<=arr.length-1;i++){
			System.out.println(arr[i].getClassName()+";"+arr[i].getMethodName()+";"+arr[i].getFileName());
		}
	}
}
public class App 
{
    public static void main( String[] args )
    {
        ThreadTest.TestString();
    }
}

执行之后的结果是:

test.TestException;getStackTrace;TestException.java

test.ThreadTest;TestString;ThreadTest.java

test.App;main;App.java

解决办法很简单,

要么使用new Exception().getStackTrace()[1];

要么使用Thread.currentThread().getStackTrace()[2]。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值