通常,对方法的内省调用需要两个阶段:首先,您需要找到要调用的目标方法,然后在目标实例上调用该方法,并为其提供参数。
在这个过程中,最昂贵的操作是在一个类中定位目标方法(例如Method method = TargetClass.getMethod(Class[] signature ...)一旦你抓住了Method对象,在一个对象上调用方法,比如:method.invoke(targetObj, param,...)是一个轻量级操作只是比直接方法调用稍微贵一点。
为了证明这一点,我只是对这三种方法进行了快速而肮脏的比较,结果如下(您需要将它们相互比较):
- 每次反射方法+调用:167ms
- 类+调用的第一个缓存方法:36 ms
- 直接调用:17ms
请注意,自省具有固定的性能成本,因此该方法执行的计算越多,这些数字就越接近。
我在以前非常重视性能的项目中使用过这种方法缓存方法。在实践中,您观察到实际的方法执行时间使得自省的成本可以忽略不计。 (cfr。Amdahal's law)
测试代码(承受快速和肮脏)
import java.lang.reflect.Method;
import java.util.Random;
/**
* Created by maasg on 5/10/14.
*/
public class Instrospection {
public static void main(String [] params) throws Exception {
Random ran = new Random();
String[] methods = new String[] {"method1", "method2", "method3"};
Target target = new Target();
// Warmup
for (int i=0; i<1000; i++) {
String methodName = methods[ran.nextInt(3)];
String param = new Integer(ran.nextInt()).toString();
Method method = Target.class.getMethod(methodName, String.class);
}
StringBuilder builder = new StringBuilder();
long t0 = System.currentTimeMillis();
for (int i=0; i<100000; i++) {
String methodName = methods[ran.nextInt(3)];
String param = new Integer(ran.nextInt()).toString();
Method method = Target.class.getMethod(methodName, String.class);
Object result = method.invoke(target, "param");
builder.append(result.toString());
}
System.out.println("Elapsed 1: "+(System.currentTimeMillis()-t0));
Method[] invokeMethods = new Method[] {
Target.class.getMethod(methods[0], String.class),
Target.class.getMethod(methods[1], String.class),
Target.class.getMethod(methods[2], String.class),
};
builder = new StringBuilder();
long t1 = System.currentTimeMillis();
for (int i=0; i<100000; i++) {
String param = new Integer(ran.nextInt()).toString();
Method method = invokeMethods[ran.nextInt(3)];
Object result = method.invoke(target, "param");
builder.append(result.toString());
}
System.out.println("Elapsed 2: "+(System.currentTimeMillis()-t1));
builder = new StringBuilder();
long t2 = System.currentTimeMillis();
for (int i=0; i<100000; i++) {
Object result = null;
String param = new Integer(ran.nextInt()).toString();
switch (ran.nextInt(3)) {
case 0: result = target.method1(param);
case 1: result = target.method2(param);
case 2: result = target.method3(param);
}
builder.append(result.toString());
}
System.out.println("Elapsed 3: "+(System.currentTimeMillis()-t2));
}
}