预备知识
了解 Java 反射基本用法
看完本文可以达到什么程度
了解 Java 反射原理及 Java 反射效率低的原因
文章概览
我们在 Java 开发中,难免会接触到反射,而在一些框架中,反射的运用更是常见。我相信,每次提到反射,大家的第一反应一定是反射效率低,尽量少使用。
但是反射的效率到底低多少?反射效率低的原因在哪里?
这篇文章就来探索一下这些问题。
由于本机上安装的是 openjdk 12,所以这里就使用 openjdk 12 源码进行分析。
我们先看结论,然后分析一下 Java 反射的原理,过程中大家可以根据结论,对源码做一些思考,然后再根据原理中的一些实现,看看 Java 反射效率低的原因。
零、先放结论
Java 反射效率低主要原因是:
- Method#invoke 方法会对参数做封装和解封操作
- 需要检查方法可见性
- 需要校验参数
- 反射方法难以内联
- JIT 无法优化
一、Java 反射原理–获取要反射的方法
1.1 反射的使用
我们先来看看 Java 反射使用的一段代码:
public class RefTest {
public static void main(String[] args) {
try {
Class clazz = Class.forName("com.zy.java.RefTest");
Object refTest = clazz.newInstance();
Method method = clazz.getDeclaredMethod("refMethod");
method.invoke(refTest);
} catch (Exception e) {
e.printStackTrace();
}
}
public void refMethod() {
}
}
我们在调用反射时,首先会创建 Class 对象,然后获取其 Method 对象,调用 invoke 方法。
获取反射方法时,有两个方法,getMethod 和 getDeclaredMethod,我们就从这两个方法开始,一步步看下反射的原理。
接下来就进入代码分析,大家做好准备。
1.2 getMethod / getDeclaredMethod
这里我们先整体看一下 getMethod 和 getDeclaredMethod 的实现。
class Class {
@CallerSensitive
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
Objects.requireNonNull(name);
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// 1. 检查方法权限
checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true);
}
// 2. 获取方法
Method method = getMethod0(name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(methodToString(name, parameterTypes));
}
// 3. 返回方法的拷贝
return getReflectionFactory().copyMethod(method);
}
@CallerSensitive
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
Objects.requireNonNull(name);
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// 1. 检查方法是权限
checkMemberAccess(sm, Member.DECLARED, Reflection.getCallerClass(), true);
}
// 2. 获取方法
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(methodToString(name, parameterTypes));
}
// 3. 返回方法的拷贝
return getReflectionFactory().copyMethod(method);
}
}
从上面的代码,我们可以看到,获取方法的流程分三步走:
- 检查方法权限
- 获取方法 Method 对象
- 返回方法的拷贝
这里主要有两个区别:
- getMethod 中 checkMemberAccess 传入的是 Member.PUBLIC,而 getDeclaredMethod
传入的是 Member.DECLARED这两个值有什么区别呢?我们看下代码中的注释:
interface Member {
/**
* Identifies the set of all public members of a class or interface,
* including inherited members.
*/
public static final int PUBLIC = 0;
/**
* Identifies the set of declared members of a class or interface.
* Inherited members are not included.
*/
public static final int DECLARED = 1;
}
注释里清楚的解释了 PUBLIC 和 DECLARED 的不同,PUBLIC 会包括所有的 public 方法,包括父类的方法,而 DECLARED 会包括所有自己定义的方法,public,protected,private 都在此,但是不包括父类的方法。
这也正是 getMethod 和 getDeclaredMethod 的区别。
- getMethod 中获取方法调用的是 getMethod0,而 getDeclaredMethod 获取方法调用的是 privateGetDeclaredMethods关于这个区别,这里简单提及一下,后面具体分析代码。
privateGetDeclaredMethods 是获取类自身定义的方法,参数是 boolean publicOnly,表示是否只获取公共方法。
private Method[] privateGetDeclaredMethods(boolean publicOnly) {
//...
}
而 getMethod0 会递归查找父类的方法,其中会调用到 privateGetDeclaredMethods 方法。
既然我们上面看了 getMethod 和 getDeclaredMethod 的区别,我们自然选择 getMethod 方法进行分析,这样可以走到整个流程。
1.3 getMethod 方法
getMethod 方法流程如下图:
class Class {
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuc