Ccna final exam - java, php, javascript, ios, cshap all in one. This is a collaboratively edited question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.
Wednesday, March 7, 2012
In Java, how do i find the caller of a method using stacktrace or reflection? [closed]
An alternative solution can be found in a comment to this request for enhancement. It uses the getClassContext() method of a custom SecurityManager and seems to be faster than the stack trace method.
The following program tests the speed of the different suggested methods (the most interesting bit is in the inner class SecurityManagerMethod):
/** * 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); }
The internal Reflection method is much faster than the others. Getting a stack trace from a newly created Throwable is faster than getting it from the current Thread. And among the non-internal ways of finding the caller class the custom SecurityManager seems to be the fastest.
Sounds like you're trying to avoid passing a reference to this into the method. Passing this is way better than finding the caller through the current stack trace. Refactoring to a more OO design is even better. You shouldn't need to know the caller. Pass a callback object if necessary.
StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace()
ReplyDeleteAccording to the Javadocs:
The last element of the array represents the bottom of the stack, which is the least recent method invocation in the sequence.
A StackTraceElement has getClassName(), getFileName(), getLineNumber() and getMethodName().
You will have to experiment to determine which index you want (probably stackTraceElements[1] or [2]).
An alternative solution can be found in a comment to this request for enhancement.
ReplyDeleteIt uses the getClassContext() method of a custom SecurityManager and seems to be faster than the stack trace method.
The following program tests the speed of the different suggested methods (the most interesting bit is in the inner class SecurityManagerMethod):
/**
* 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.");
}
}
An example of the output from my 2.4 GHz Intel Core 2 Duo MacBook running Java 1.6.0_17:
Reflection: 10.195 ms.
Current Thread StackTrace: 5886.964 ms.
Throwable StackTrace: 4700.073 ms.
SecurityManager: 1046.804 ms.
The internal Reflection method is much faster than the others. Getting a stack trace from a newly created Throwable is faster than getting it from the current Thread. And among the non-internal ways of finding the caller class the custom SecurityManager seems to be the fastest.
Sounds like you're trying to avoid passing a reference to this into the method. Passing this is way better than finding the caller through the current stack trace. Refactoring to a more OO design is even better. You shouldn't need to know the caller. Pass a callback object if necessary.
ReplyDelete