/**
* Test the speed of various methods for getting the caller class name
*/
public class TestGetCallerClassName {
/**
* Abstract class for testing different methods of getting the caller class name
*/
private static abstract class GetCallerClassNameMethod {
public abstract String getCallerClassName(int callStackDepth);
public abstract String getMethodName();
}
/**
* Uses the internal Reflection class
*/
private static class ReflectionMethod extends GetCallerClassNameMethod {
public String getCallerClassName(int callStackDepth) {
return sun.reflect.Reflection.getCallerClass(callStackDepth).getName();
}
public String getMethodName() {
return "Reflection";
}
}
/**
* Get a stack trace from the current thread
*/
private static class ThreadStackTraceMethod extends GetCallerClassNameMethod {
public String getCallerClassName(int callStackDepth) {
return Thread.currentThread().getStackTrace()[callStackDepth].getClassName();
}
public String getMethodName() {
return "Current Thread StackTrace";
}
}
/**
* Get a stack trace from a new Throwable
*/
private static class ThrowableStackTraceMethod extends GetCallerClassNameMethod {
public String getCallerClassName(int callStackDepth) {
return new Throwable().getStackTrace()[callStackDepth].getClassName();
}
public String getMethodName() {
return "Throwable StackTrace";
}
}
/**
* Use the SecurityManager.getClassContext()
*/
private static class SecurityManagerMethod extends GetCallerClassNameMethod {
public String getCallerClassName(int callStackDepth) {
return mySecurityManager.getCallerClassName(callStackDepth);
}
public String getMethodName() {
return "SecurityManager";
}
/**
* A custom security manager that exposes the getClassContext() information
*/
static class MySecurityManager extends SecurityManager {
public String getCallerClassName(int callStackDepth) {
return getClassContext()[callStackDepth].getName();
}
}
private final static MySecurityManager mySecurityManager =
new MySecurityManager();
}
/**
* Test all four methods
*/
public static void main(String[] args) {
testMethod(new ReflectionMethod());
testMethod(new ThreadStackTraceMethod());
testMethod(new ThrowableStackTraceMethod());
testMethod(new SecurityManagerMethod());
}
private static void testMethod(GetCallerClassNameMethod method) {
long startTime = System.nanoTime();
String className = null;
for (int i = 0; i < 1000000; i++) {
className = method.getCallerClassName(2);
}
printElapsedTime(method.getMethodName(), startTime);
}
private static void printElapsedTime(String title, long startTime) {
System.out.println(title + ": " + ((double)(System.nanoTime() - startTime))/1000000 + " ms.");
}
}
运行Java 1.6.0_17的2.4 GHz Intel Core 2 Duo MacBook的输出示例:
sun.reflect.Reflection
/** Returns the class of the method <code>realFramesToSkip</code>
frames up the stack (zero-based), ignoring frames associated
with java.lang.reflect.Method.invoke() and its implementation.
The first frame is that associated with this method, so
<code>getCallerClass(0)</code> returns the Class object for
sun.reflect.Reflection. Frames associated with
java.lang.reflect.Method.invoke() and its implementation are
completely ignored and do not count toward the number of "real"
frames skipped. */
public static native Class getCallerClass(int realFramesToSkip);
/**
* Get the method name for a depth in call stack.
* Utility function
* @param depth depth in the call stack (0 means current method, 1 means call method, ...)
* @return method name
*/
public static String getMethodName(final int depth)
{
final StackTraceElement[] ste = new Throwable().getStackTrace();
//System. out.println(ste[ste.length-depth].getClassName()+"#"+ste[ste.length-depth].getMethodName());
return ste[ste.length - depth].getMethodName();
}
/**
* Returns the first "[class#method(line)]: " of the first class not equal to "StackTraceUtils".
* From the Stack Trace.
* @return "[class#method(line)]: " (never empty, first class past StackTraceUtils)
*/
public static String getClassMethodLine()
{
return getClassMethodLine(null);
}
/**
* Returns the first "[class#method(line)]: " of the first class not equal to "StackTraceUtils" and aclass.
* Allows to get past a certain class.
* @param aclass class to get pass in the stack trace. If null, only try to get past StackTraceUtils.
* @return "[class#method(line)]: " (never empty, because if aclass is not found, returns first class past StackTraceUtils)
*/
public static String getClassMethodLine(final Class aclass)
{
final StackTraceElement st = getCallingStackTraceElement(aclass);
final String amsg = "[" + st.getClassName() + "#" + st.getMethodName() + "(" + st.getLineNumber()
+")] <" + Thread.currentThread().getName() + ">: ";
return amsg;
}
/**
* Returns the first stack trace element of the first class not equal to "StackTraceUtils" or "LogUtils" and aClass.
* Stored in array of the callstack.
* Allows to get past a certain class.
* @param aclass class to get pass in the stack trace. If null, only try to get past StackTraceUtils.
* @return stackTraceElement (never null, because if aClass is not found, returns first class past StackTraceUtils)
* @throws AssertionFailedException if resulting statckTrace is null (RuntimeException)
*/
public static StackTraceElement getCallingStackTraceElement(final Class aclass)
{
final Throwable t = new Throwable();
final StackTraceElement[] ste = t.getStackTrace();
int index = 1;
final int limit = ste.length;
StackTraceElement st = ste[index];
String className = st.getClassName();
boolean aclassfound = false;
if(aclass == null)
{
aclassfound = true;
}
StackTraceElement resst = null;
while(index < limit)
{
if(shouldExamine(className, aclass) == true)
{
if(resst == null)
{
resst = st;
}
if(aclassfound == true)
{
final StackTraceElement ast = onClassfound(aclass, className, st);
if(ast != null)
{
resst = ast;
break;
}
}
else
{
if(aclass != null && aclass.getName().equals(className) == true)
{
aclassfound = true;
}
}
}
index = index + 1;
st = ste[index];
className = st.getClassName();
}
if(resst == null)
{
//Assert.isNotNull(resst, "stack trace should null"); //NO OTHERWISE circular dependencies
throw new AssertionFailedException(StackTraceUtils.getClassMethodLine() + " null argument:" + "stack trace should null"); //$NON-NLS-1$
}
return resst;
}
static private boolean shouldExamine(String className, Class aclass)
{
final boolean res = StackTraceUtils.class.getName().equals(className) == false && (className.endsWith("LogUtils"
) == false || (aclass !=null && aclass.getName().endsWith("LogUtils")));
return res;
}
static private StackTraceElement onClassfound(Class aclass, String className, StackTraceElement st)
{
StackTraceElement resst = null;
if(aclass != null && aclass.getName().equals(className) == false)
{
resst = st;
}
if(aclass == null)
{
resst = st;
}
return resst;
}
private void parseExceptionContents(
final Exception exception,
final OutputStream out)
{
final StackTraceElement[] stackTrace = exception.getStackTrace();
int index = 0;
for (StackTraceElement element : stackTrace)
{
final String exceptionMsg =
"Exception thrown from " + element.getMethodName()
+ " in class " + element.getClassName() + " [on line number "
+ element.getLineNumber() + " of file " + element.getFileName() + "]";
try
{
out.write((headerLine + newLine).getBytes());
out.write((headerTitlePortion + index++ + newLine).getBytes() );
out.write((headerLine + newLine).getBytes());
out.write((exceptionMsg + newLine + newLine).getBytes());
out.write(
("Exception.toString: " + element.toString() + newLine).getBytes());
}
catch (IOException ioEx)
{
System.err.println(
"IOException encountered while trying to write "
+ "StackTraceElement data to provided OutputStream.\n"
+ ioEx.getMessage() );
}
}
}
6
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
class DBConnection {
String createdBy = null;
DBConnection(Throwable whoCreatedMe) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
PrintWriter pw = new PrintWriter(os);
whoCreatedMe.printStackTrace(pw);
try {
createdBy = os.toString();
pw.close();
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class ThrowableTest {
public static void main(String[] args) {
Throwable createdBy = new Throwable(
"Connection created from DBConnectionManager");
DBConnection conn = new DBConnection(createdBy);
System.out.println(conn.createdBy);
}
}
或
public static interface ICallback<T> { T doOperation(); }
public class TestCallerOfMethod {
public static <T> T callTwo(final ICallback<T> c){
// Pass the object created at callee to the caller
// From the passed object we can get; what is the callee name like below.
System.out.println(c.getClass().getEnclosingMethod().getName());
return c.doOperation();
}
public static boolean callOne(){
ICallback callBackInstance = new ICallback(Boolean){
@Override
public Boolean doOperation()
{
return true;
}
};
return callTwo(callBackInstance);
}
public static void main(String[] args) {
callOne();
}
}
0
使用这种方法: -
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
stackTraceElement e = stacktrace[2];//maybe this number needs to be corrected
System.out.println(e.getMethodName());
方法示例代码的调用者在这里: -
public class TestString {
public static void main(String[] args) {
TestString testString = new TestString();
testString.doit1();
testString.doit2();
testString.doit3();
testString.doit4();
}
public void doit() {
StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
StackTraceElement e = stacktrace[2];//maybe this number needs to be corrected
System.out.println(e.getMethodName());
}
public void doit1() {
doit();
}
public void doit2() {
doit();
}
public void doit3() {
doit();
}
public void doit4() {
doit();
}
}
12 回答
根据Javadocs:
StackTraceElement
有getClassName()
,getFileName()
,getLineNumber()
和getMethodName()
.您将不得不尝试确定您想要的索引(可能是
stackTraceElements[1]
或[2]
) .可以在对this request for enhancement的评论中找到替代解决方案 . 它使用自定义
SecurityManager
的getClassContext()
方法,并且似乎比堆栈跟踪方法更快 .以下程序测试不同建议方法的速度(最有趣的位在内部类
SecurityManagerMethod
):运行Java 1.6.0_17的2.4 GHz Intel Core 2 Duo MacBook的输出示例:
内部Reflection方法比其他方法快得多 . 从新创建的
Throwable
获取堆栈跟踪比从当前Thread
获取堆栈跟踪更快 . 在查找调用者类的非内部方法中,自定义SecurityManager
似乎是最快的 .更新
正如this comment中的 lyomi 指出的那样,
sun.reflect.Reflection.getCallerClass()
方法默认在Java 7更新40中被禁用,并在Java 8中完全删除 . 在this issue in the Java bug database中了解更多相关信息 .更新2
正如 zammbi 所发现的那样,Oracle被forced to back out of the change删除了
sun.reflect.Reflection.getCallerClass()
. 它仍然可以在Java 8中使用(但不推荐使用) .更新3
3年后:使用当前JVM更新时序 .
听起来你正试图避免将对
this
的引用传递给方法 . 传递this
比通过当前堆栈跟踪查找调用方更好 . Refactoring to a more OO design is even better. 您不需要知道呼叫者 . 必要时传递回调对象 .以下是我在本主题中显示的提示中创建的代码的一部分 . 希望能帮助到你 .
(随意提出改进此代码的任何建议,请告诉我)
柜台:
对象:
Java 9 - JEP 259:Stack-Walking API
JEP 259为堆栈遍历提供了一种有效的标准API,允许轻松过滤和延迟访问堆栈跟踪中的信息 . 在Stack-Walking API之前,访问堆栈帧的常用方法是:
使用这些API通常效率低下:
为了找到直接调用者的类,首先获取
StackWalker
:然后调用
getCallerClass()
:或
walk
StackFrame
s并获得第一个前面的StackFrame
:Oneliner :
请注意,您可能需要将2替换为1 .
这种方法做了同样的事情,但更简单,可能更高效,如果你使用反射,它会自动跳过这些帧 . 唯一的问题是它可能不存在于非Sun JVM中,尽管它包含在JRockit 1.4的运行时类 - > 1.6中 . (重点是,它不是公共课) .
至于
realFramesToSkip
值应该是java.lang.System
的Sun 1.5和1.6 VM版本,有一个名为getCallerClass()的包保护方法,它调用sun.reflect.Reflection.getCallerClass(3)
,但在我的帮助实用程序类中,我使用了4,因为有一个添加的框架帮助程序类调用 .例如,如果您尝试将调用方法行用于调试目的,则需要通过Utility类来编写这些静态方法:
(旧的java1.4代码,只是为了说明潜在的StackTraceElement用法)
我之前做过这个 . 您可以创建一个新的异常并在其上抓取堆栈跟踪而不抛出它,然后检查堆栈跟踪 . 正如另一个答案所说的那样,它的成本非常高 - 不要在紧密的环路中进行 .
我之前已经完成了在应用程序上的日志实用程序,其中性能并不重要(性能很少,实际上 - 只要您将结果显示为快速按钮单击等操作) .
在您获得堆栈跟踪之前,异常只有.printStackTrace()所以我不得不将System.out重定向到我自己的创建流,然后(new Exception()) . printStackTrace();重定向System.out并解析流 . 好玩的东西 .
或
使用这种方法: -
方法示例代码的调用者在这里: -