您可以使用方法句柄。它的Javadoc写道:
使用查找 API 中的工厂方法,可以将由核心反射 API 对象表示的任何类成员转换为行为等效的方法句柄。例如,可以使用 Lookup.unreflect 将反射方法转换为方法句柄。生成的方法句柄通常提供对基础类成员的更直接、更有效的访问。
虽然这将减少开销,但方法句柄仍然会阻止 JVM 可能采用的某些优化(例如方法内联),如果调用是使用通常的(非反射式)字节代码指令进行的。这种优化是否有益取决于您如何使用该方法(如果该代码路径始终调用相同的方法,则内联可能会有所帮助,如果每次都是不同的方法,则可能不然)。
以下微基准标记可能会让您大致了解反射、方法句柄和直接调用的相对性能:
package tools.bench;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.math.BigDecimal;
public abstract class Bench {
final String name;
public Bench(String name) {
this.name = name;
}
abstract int run(int iterations) throws Throwable;
private BigDecimal time() {
try {
int nextI = 1;
int i;
long duration;
do {
i = nextI;
long start = System.nanoTime();
run(i);
duration = System.nanoTime() - start;
nextI = (i << 1) | 1;
} while (duration < 100000000 && nextI > 0);
return new BigDecimal((duration) * 1000 / i).movePointLeft(3);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() {
return name + "\t" + time() + " ns";
}
static class C {
public Integer foo() {
return 1;
}
}
static final MethodHandle sfmh;
static {
try {
Method m = C.class.getMethod("foo");
sfmh = MethodHandles.lookup().unreflect(m);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws Exception {
final C invocationTarget = new C();
final Method m = C.class.getMethod("foo");
final Method am = C.class.getMethod("foo");
am.setAccessible(true);
final MethodHandle mh = sfmh;
Bench[] marks = {
new Bench("reflective invocation (without setAccessible)") {
@Override int run(int iterations) throws Throwable {
int x = 0;
for (int i = 0; i < iterations; i++) {
x += (Integer) m.invoke(invocationTarget);
}
return x;
}
},
new Bench("reflective invocation (with setAccessible)") {
@Override int run(int iterations) throws Throwable {
int x = 0;
for (int i = 0; i < iterations; i++) {
x += (Integer) am.invoke(invocationTarget);
}
return x;
}
},
new Bench("methodhandle invocation") {
@Override int run(int iterations) throws Throwable {
int x = 0;
for (int i = 0; i < iterations; i++) {
x += (Integer) mh.invokeExact(invocationTarget);
}
return x;
}
},
new Bench("static final methodhandle invocation") {
@Override int run(int iterations) throws Throwable {
int x = 0;
for (int i = 0; i < iterations; i++) {
x += (Integer) sfmh.invokeExact(invocationTarget);
}
return x;
}
},
new Bench("direct invocation") {
@Override int run(int iterations) throws Throwable {
int x = 0;
for (int i = 0; i < iterations; i++) {
x += invocationTarget.foo();
}
return x;
}
},
};
for (Bench bm : marks) {
System.out.println(bm);
}
}
}
在我有点过时的笔记本上
java version "1.7.0_02"
Java(TM) SE Runtime Environment (build 1.7.0_02-b13)
Java HotSpot(TM) Client VM (build 22.0-b10, mixed mode, sharing)
这张照片:
reflective invocation (without setAccessible) 568.506 ns
reflective invocation (with setAccessible) 42.377 ns
methodhandle invocation 27.461 ns
static final methodhandle invocation 9.402 ns
direct invocation 9.363 ns
更新:正如 Irreputable 所指出的,服务器 VM 具有一些不同的性能特征,因此,只有在您可以将 MethodHandle 放在静态最终字段中时才会有所帮助,在这种情况下,VM 可以内联调用:
reflective invocation (without setAccessible) 9.736 ns
reflective invocation (with setAccessible) 7.113 ns
methodhandle invocation 26.319 ns
static final methodhandle invocation 0.045 ns
direct invocation 0.044 ns
我建议您衡量您的特定用例。