我可以在静态方法中获得对调用类的引用吗?

yeg*_*256 4 java

我有一个静态Java方法,我想知道谁是它的调用者.是否有可能用Java获取此信息?

T.J*_*der 12

这是可能的,但它很昂贵,并且在任何远程像正常情况一样,这是一个非常糟糕的主意.你几乎肯定想以另一种方式解决你正在解决的问题.(如果你发布一个关于你正在解决的问题的新问题,我打赌有人会帮助你这样做!:-))

有两种方法可以生成堆栈跟踪:

1)通过 Throwable

...通过抛出并捕获异常,然后使用 Exception#getStackTrace

try {
    throw new Exception();
}
catch (Exception e) {
    // Get the stack trace
    StackTraceElement[] entries = e.getStackTrace();
}
Run Code Online (Sandbox Code Playgroud)

......或者lscoughlin指出(请投票给他/她),更直接地通过以下方式new Throwable:

StackTraceElement[] entries = new Throwable().getStackTrace();
Run Code Online (Sandbox Code Playgroud)

然后,每个StackTraceElements都会为您提供有关堆栈跟踪中该点的信息.在你的情况,你可能想看看StackTraceElementgetClassName方法.如果你真的需要对调用类对象的引用,你可以将该类名称字符串传递给Class.forName但是要注意,在复杂环境中,如果调用者使用了有趣的东西,你可能会获得该类的不同实例(或者没有实例)班级装载机.

2)通过使用 Thread#getStackTrace

MRalwasser在下面有用地指出在Java 5或更高版本中(我猜你可能正在使用Java 5或更高版本),你可以使用Thread.currentThread().getStackTrace()而不是实际抛出异常.我不知道它是否会更轻量级(因为获得堆栈跟踪很可能是抛出异常的代价),但它绝对更清晰.

在一个快速而肮脏的测试中,反直觉地实际抛出异常似乎比通过更快Thread(并且几乎相同new Throwable),但JVM应用程序的天真剖析的变幻莫测的记录,你的里程可能会有所不同......

但同样,不仅让堆栈跟踪成为一个昂贵的过程,而且使用有关调用方没有通过方法签名传递给您的调用方的信息是一个严重的设计问题,除了(比如)使用的调试器或错误跟踪器仅用于开发.这里的主要答案必须是:不要这样做,除非你有充分的理由.

  • 如果正在使用Java 5或更高版本,则应使用Thread.getCurrentThread().getStackTrace().虽然它是相同的想法,但它应该更轻一点. (2认同)

小智 8

很抱歉恢复这个线程,但我已经使用了很长时间的机制来完成同样的事情,而没有繁琐的堆栈跟踪使用.


class ClassloaderUtil {

    public static Class getCallingClass() {
        return CallerResolver.getCallerClass(2);
    }

    private static final class CallerResolver extends SecurityManager {
        private static final CallerResolver CALLER_RESOLVER = new CallerResolver();
        private static final int CALL_CONTEXT_OFFSET = 3; // may need to change if this class is redesigned
        protected Class[] getClassContext() {
            return super.getClassContext();
        }

        /*
        * Indexes into the current method call context with a given
        * offset.
        */
        private static Class getCallerClass(int callerOffset) {
            return CALLER_RESOLVER.getClassContext()[CALL_CONTEXT_OFFSET + callerOffset];
        }
        private static int getContextSize() {
            return CALLER_RESOLVER.getClassContext().length - CALL_CONTEXT_OFFSET;
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

然后使用它就像这样简单:

public class ClassloaderUtilTest {

    @Test
    public void test() {
        Class c = ClassloaderUtil.getCallingClass();
        Assert.assertNotNull(c);
        c = foo();
        Assert.assertNotNull(c);
    }

    private Class foo() {
        return ClassloaderUtil.getCallingClass();
    }
}
Run Code Online (Sandbox Code Playgroud)

第一个类将是一些junit框架类,而foo()将返回ClassloaderUtilTest作为类.

这绝对不是完美的.但是,它确实有其随机用途.我同意那些已经回答过这个问题的人,因为这非常昂贵.


lsc*_*lin 5

只有新的可收起的更容易,更安全,如:

   Throwable t = new Throwable();
   StackTraceElement directCaller = t.getStackTrace()[1];
Run Code Online (Sandbox Code Playgroud)

但总的来说 - 它仍然是一个糟糕的想法,而且价格昂贵.