如何在Java中获取调用者类

For*_*med 57 java

我想得到方法的调用者类,即

class foo{

  bar();

}
Run Code Online (Sandbox Code Playgroud)

在方法栏中,我需要获取类名foo,我找到了这个方法:

Class clazz = sun.reflect.Reflection.getCallerClass(1);
Run Code Online (Sandbox Code Playgroud)

但是,即使getCallerClasspublic,当我试着调用它时,Eclipse说:

访问限制:由于对所需库C:\ Program Files\Java\jre7\lib\rt.jar的限制,无法访问Reflection类型的方法getCallerClass()

还有其他选择吗?

Den*_*ret 92

您可以生成堆栈跟踪并使用StackTraceElements中的信息.

例如,实用程序类可以返回调用类名:

public class KDebug {
    public static String getCallerClassName() { 
        StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
        for (int i=1; i<stElements.length; i++) {
            StackTraceElement ste = stElements[i];
            if (!ste.getClassName().equals(KDebug.class.getName()) && ste.getClassName().indexOf("java.lang.Thread")!=0) {
                return ste.getClassName();
            }
        }
        return null;
     }
}
Run Code Online (Sandbox Code Playgroud)

如果你调用KDebug.getCallerClassName()bar(),你会得到"foo".

现在假设你想知道调用方法的类bar(这更有趣,也许你真正想要的).你可以使用这个方法:

public static String getCallerCallerClassName() { 
    StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
    String callerClassName = null;
    for (int i=1; i<stElements.length; i++) {
        StackTraceElement ste = stElements[i];
        if (!ste.getClassName().equals(KDebug.class.getName())&& ste.getClassName().indexOf("java.lang.Thread")!=0) {
            if (callerClassName==null) {
                callerClassName = ste.getClassName();
            } else if (!callerClassName.equals(ste.getClassName())) {
                return ste.getClassName();
            }
        }
    }
    return null;
 }
Run Code Online (Sandbox Code Playgroud)

这是调试吗?如果没有,可能会有更好的解决方案来解决您的问题.

  • 这个答案远远优于公认的答案,我相信,更能解决原始问题的意图.至少,它解决了把我带到这里的问题.谢谢. (13认同)

Hit*_*its 32

要获得调用者/被调用者类名称使用下面的代码,它对我来说很好.

String callerClassName = new Exception().getStackTrace()[1].getClassName();
String calleeClassName = new Exception().getStackTrace()[0].getClassName();
Run Code Online (Sandbox Code Playgroud)

  • 这并不完全等同于`Reflection.getCallerClass(1)`,因为它给出了类的名称而不是实际的类.假设我想知道哪个类加载器加载了我的调用者?`Reflection.getCallerClass(1).getClassLoader()`可以告诉我,这种方法不行. (3认同)

Ats*_*lgs 28

这高度取决于你正在寻找的......但是这应该得到直接在这个对象中调用这个方法的类和方法.

  • index 0 =线程
  • index 1 =这个
  • index 2 =直接来电,可以是自我.
  • index 3 ... n =相互调用以获取索引2及以下的类和方法.

对于类/方法/文件名:

Thread.currentThread().getStackTrace()[2].getClassName();
Thread.currentThread().getStackTrace()[2].getMethodName();
Thread.currentThread().getStackTrace()[2].getFileName();
Run Code Online (Sandbox Code Playgroud)

上课:

Class.forName(Thread.currentThread().getStackTrace()[2].getClassName())
Run Code Online (Sandbox Code Playgroud)

FYI:Class.forName()抛出一个非运行时的ClassNotFoundException.你需要尝试捕获.

此外,如果您要忽略类本身内的调用,则必须添加一些带逻辑的循环来检查该特定内容.

像...这样的东西(我没有测试过这段代码所以要小心)

StackTraceElement[] stes = Thread.currentThread().getStackTrace();
for(int i=2;i<stes.length;i++)
  if(!stes[i].getClassName().equals(this.getClass().getName()))
    return stes[i].getClassName();
Run Code Online (Sandbox Code Playgroud)

只是一个想法....

  • 答案在以下注释中:/sf/ask/791476801/?noredirect=1#comment58209278_35083181 (2认同)

Lun*_*una 12

SecurityManager有一个受保护的方法getClassContext

通过创建扩展SecurityManager的实用程序类,您可以访问它.

public class CallingClass extends SecurityManager {
    public static final CallingClass INSTANCE = new CallingClass();

    public Class[] getCallingClasses() {
        return getClassContext();
    }
}
Run Code Online (Sandbox Code Playgroud)

使用CallingClass.INSTANCE.getCallingClasses()检索调用类.

还有一个小型图书馆(免责声明:我的)WhoCalled公开了这些信息.它在可用时使用Reflection.getCallerClass,否则回退到SecurityManager.

  • TL;基准测试的DR:Thread.currentThread().getStackTrace()比SecurityManager.getClassContext()快20倍,它比Reflection.getCallingClass()慢5倍,需要~85ns/operation. (6认同)
  • @mwoodman 添加了 JMH 基准。结果在这里:https://github.com/nallar/WhoCalled/issues/1#issuecomment-180750822 (2认同)

nul*_*dev 5

我知道这是一个古老的问题,但我相信提问者想要上课,而不是课堂名称.我写了一个小方法来获得实际的课程.它有点像学习,可能并不总是有效,但有时当你需要实际的课时,你将不得不使用这种方法......

/**
     * Get the caller class.
     * @param level The level of the caller class.
     *              For example: If you are calling this class inside a method and you want to get the caller class of that method,
     *                           you would use level 2. If you want the caller of that class, you would use level 3.
     *
     *              Usually level 2 is the one you want.
     * @return The caller class.
     * @throws ClassNotFoundException We failed to find the caller class.
     */
    public static Class getCallerClass(int level) throws ClassNotFoundException {
        StackTraceElement[] stElements = Thread.currentThread().getStackTrace();
        String rawFQN = stElements[level+1].toString().split("\\(")[0];
        return Class.forName(rawFQN.substring(0, rawFQN.lastIndexOf('.')));
    }
Run Code Online (Sandbox Code Playgroud)

  • 发问者写道:“……我需要获取班级名称……” (2认同)