java反射

首先要知道RTTI(run-time type identification),即运行时类型识别,用于在运行时识别一个对象的类型和类信息。
不同于编译期间就能确定的对象类型及类信息,java反射允许开发者在运行时发现和使用类的信息
在运行时,通过类的全限定名,可以构造出制定对象,可以调用该对象的任意属性和方法。
java的反射机制主要提供以下功能:

  • 运行时判断任意一个对象所属的类
  • 运行时构造任意一个类对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时调用任意一个对象的方法

反射基础

在开始讲述java反射之前,先来看一下Class类。

1. Class 类

Class 类位于java.lang中。该类的实例表示java应用运行时的类或者接口。运行时每个类对象在jvm里的表示都是一个Class对象。可以通过类名.class类型.getClassClass.forName('类全限定名')等方法获取Class对象。另外数组也被映射为class对象的一个类,所有具有相同元素类型和维度的数组都共享该class对象。基本类型boolean、byte、char、short、int、long、float、double和关键字void也是class对象。

Class类对象的作用是运行时提供某个对象的类型信息,供反射使用。
来看一下Class的定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
private static final int ANNOTATION= 0x00002000;
private static final int ENUM = 0x00004000;
private static final int SYNTHETIC = 0x00001000;

private static native void registerNatives();
static {
registerNatives();
}

/*
* Private constructor. Only the Java Virtual Machine creates Class objects.
* This constructor is not used and prevents the default constructor being
* generated.
*/
private Class(ClassLoader loader, Class<?> arrayComponentType) {
// Initialize final field for classLoader. The initialization value of non-null
// prevents future JIT optimizations from assuming this final field is null.
classLoader = loader;
componentType = arrayComponentType;
}
}

上述代码中出现了native关键字,这里是java会在JNI库中根据函数名查找对应的JNI函数。如果没找到,会报错。如果找到了,则会在native函数与JNI函数之间建立关联关系,其实就是保存JNI函数的函数指针。
JNI相关知识可以看这篇博文:JNI基础知识及实现

上述代码反馈了如下信息:

  1. Class提供的构造方法包含两个参数,ClassLoaderClass。这个构造方法仅有jvm调用。
  2. 开发者编写的类被编译后会生成Class对象,该对象被保存在同名的.class字节码文件中,用以标识此类的类型信息,
  3. 每个通过关键字class标识的类,在内存中有且仅有一个与之对应的Class对象用以描述其类型信息,无论有多少个实例,都对应这个生成的Class对象。该对象存放于方法区中。【这里仅指代由同一个类加载器加载的类】

2. 类加载

类加载机制和类的生命周期在之前的博文中有详细描述,可见:
java类加载机制和类加载器
类加载过程:
类的生命周期
类加载

反射的使用

反射提供了如下功能:

  • 在运行时判断任意一个对象所属的类。
  • 在运行时构造任意一个类的对象。
  • 在运行时判断任意一个类所具有的成员变量和方法。
  • 在运行时调用任意一个对象的方法。

获取Class类对象

有三种获取Class类对象的方式:

  1. Class.forName(全限定类名) 静态方法。源代码阶段(.java -> .class)
  2. 类名.class() 方法(基本类型只可该方法获得Class对象)。加载阶段(.class加载到内存中)
  3. 对象.getClass() 方法。运行阶段(内存中运行 A a = new A();)

如下示例可以看出,如果一个类由同一个类加载器加载,则方法区中的 Class对象一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void main(String[] args) throws ClassNotFoundException {
// 1. 使用 Class.forName 获取 类对象
Class<?> aClass = Class.forName("mqray.learn.cn.service.entity.PersonEntity");
System.out.println(aClass);

// 2. 使用 instance.getClass() 获取类对象
PersonEntity personEntity = new PersonEntity();
Class<? extends PersonEntity> aClass1 = personEntity.getClass();
System.out.println(aClass1);
// Class 存于方法区,一个类由同一个类加载器加载,则方法区中的 Class对象一致
System.out.println(aClass == aClass1); // true

// 3. 使用 className.class 获取类对象
Class<PersonEntity> personEntityClass = PersonEntity.class;
System.out.println(personEntityClass);
System.out.println(personEntityClass==aClass); // true
}

Constructor类及其用法

在java中我们经常使用类构造方法来创建对象,这里就是先通过Class对象获取指定的构造器,然后调用构造器方法创建该类对象。
Class类中提供如下获取类构造方法的方法:

1
2
3
4
public Constructor<T> getConstructor(Class<?>... parameterTypes) // 返回指定参数类型、由public声明的构造函数 如果没有会抛出NoSuchMethodException异常
public Constructor<?>[] getConstructors() // 获取所有被public修饰的构造器数组 [只返回当前类的构造器]
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) // 返回指定参数类型、所有声明的构造函数对象, 包含private、protected [只返回当前类的构造器]
public Constructor<?>[] getDeclaredConstructors() // 返回所有声明的构造函数对象
没有父类继承情况

有如下示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// 1. 实例化对象 - 无参构造器
PersonEntity entity = new PersonEntity();
System.out.println(entity);
// 2. 获取无参构造器
Class<?> aClass2 = Class.forName("mqray.learn.cn.service.entity.PersonEntity");
Constructor<?> nonPrmConstructor = aClass2.getConstructor();
Object o = nonPrmConstructor.newInstance();

PersonEntity entity1 = (PersonEntity) o;
System.out.println(entity1);


// 2. 有参构造器 -- 这里会抛出 NoSuchMethodException 异常, 因为 该构造函数由 private声明
// Constructor<?> twoPrmConstructor = aClass2.getConstructor(String.class, int.class);
// Object o1 = twoPrmConstructor.newInstance("mqray", "24");
// System.out.println(o1);

// 三个参数的构造器是public修饰的需要用 getDeclaredConstructor 方法获取
Constructor<?> threePrmDeclaredConstructor = aClass2.getDeclaredConstructor(String.class, int.class,
String.class);
Object o2 = threePrmDeclaredConstructor.newInstance("maray", 24, "male");
System.out.println(o2);
System.out.println("-----------split----------");
// 获取所有声明的构造器
Constructor<?>[] constructors = aClass2.getConstructors();
for (Constructor constructor: constructors){
System.out.println(constructor);
}

System.out.println("-----------split----------");

Constructor<?>[] declaredConstructors = aClass2.getDeclaredConstructors();
for (Constructor constructor: declaredConstructors){
System.out.println(constructor);
}

}

输出结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
PersonEntity(name=null, age=0, height=null, gender=null, occupation=null)
PersonEntity(name=null, age=0, height=null, gender=null, occupation=null)
PersonEntity(name=maray, age=24, height=null, gender=male, occupation=null)
-----------split----------
public mqray.learn.cn.service.entity.PersonEntity(java.lang.String,int,java.lang.Double,java.lang.String,java.lang.String)
public mqray.learn.cn.service.entity.PersonEntity(java.lang.String,int,java.lang.String)
public mqray.learn.cn.service.entity.PersonEntity()
-----------split----------
public mqray.learn.cn.service.entity.PersonEntity(java.lang.String,int,java.lang.Double,java.lang.String,java.lang.String)
public mqray.learn.cn.service.entity.PersonEntity(java.lang.String,int,java.lang.String)
private mqray.learn.cn.service.entity.PersonEntity(java.lang.String,int)
public mqray.learn.cn.service.entity.PersonEntity()

可以看到getConstructorsgetDeclaredConstructors相差一个private mqray.learn.cn.service.entity.PersonEntity(java.lang.String,int),恰对应上由private修饰的private PersonEntity(String name, int age)构造方法。

存在父类继承关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) throws ClassNotFoundException {

Class<?> aClass = Class.forName("mqray.learn.cn.service.entity.WriterEntity");
System.out.println("-----------split----------");
Constructor<?>[] constructors = aClass.getConstructors();
for (Constructor constructor: constructors){
System.out.println(constructor);
}
System.out.println("-----------split----------");
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor constructor: declaredConstructors){
System.out.println(constructor);
}

}

对应输出为:

1
2
3
-----------split----------
-----------split----------
private mqray.learn.cn.service.entity.WriterEntity(java.lang.String,int,java.lang.String)

可以看到,子类中调用getConstructors没有获取到由public声明的类构造器,而调用getDeclaredConstructors时也仅获取到一个由private修饰的子类构造器,所以这里可以证实
getConstructors 和 getDeclaredConstructors仅能获取到子类自身的类构造器

其实上述结论在两个方法的说明里都有:

1
the array of Constructor objects representing the public constructors of this class

Field类及其用法

关于类字段的主要有如下方法:

1
2
3
4
1. getFields() 获取当前类 由 public 关键字声明的字段,如不存在会抛出 NoSuchFieldException
2. public Field getField(String name) 获取指定名称的字段(由public声明), 如不存在会抛出 NoSuchFieldException
3. getDeclaredFields 获取当前类 所有声明的属性
4. public Field getDeclaredField(String name) 获取指定名称的字段(由 privateprotectedpublic 声明)

这里要注意的是 getFields()能够获取到父类中被public声明的属性,getDeclaredField也不能获取到被private声明的属性,只能获取到当前子类的属性。

无继承关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class TestReflectField {

public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {

Class<?> aClass = Class.forName("mqray.learn.cn.service.entity.PersonEntity");
System.out.println("------- ******getFields***** ----------");
Field[] fields = aClass.getFields();
for(Field field: fields){
System.out.println(field);
}
System.out.println("-------*****getField******----------");
// 抛出 NoSuchFieldException 异常,因为 name 字段 是由private声明的
// Field field = aClass.getField("name");
Field number = aClass.getField("NUMBER");
System.out.println(number);
System.out.println("-------*****getDeclaredFields******----------");
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField: declaredFields){
System.out.println(declaredField);
}
System.out.println("-------*******getDeclaredField****----------");

Field declaredField = aClass.getDeclaredField("NUMBER");
System.out.println(declaredField);
}
}

对应的输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
------- ******getFields***** ----------
public static final java.lang.Integer mqray.learn.cn.service.entity.PersonEntity.NUMBER
-------*****getField******----------
public static final java.lang.Integer mqray.learn.cn.service.entity.PersonEntity.NUMBER
-------*****getDeclaredFields******----------
public static final java.lang.Integer mqray.learn.cn.service.entity.PersonEntity.NUMBER
private java.lang.String mqray.learn.cn.service.entity.PersonEntity.name
private int mqray.learn.cn.service.entity.PersonEntity.age
private java.lang.Double mqray.learn.cn.service.entity.PersonEntity.height
private java.lang.String mqray.learn.cn.service.entity.PersonEntity.gender
private java.lang.String mqray.learn.cn.service.entity.PersonEntity.occupation
-------*******getDeclaredField****----------
private java.lang.String mqray.learn.cn.service.entity.PersonEntity.name
存在继承关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class TestReflectFieldWithParent {

public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {

Class<?> aClass = Class.forName("mqray.learn.cn.service.entity.WriterEntity");
System.out.println("------- ******getFields***** ----------");
// 这里能获取到父类的 NUMBER 属性,number是由 public 声明的 说明 是可以获取到父类的属性值的
Field[] fields = aClass.getFields();
for(Field field: fields){
System.out.println(field);
}
System.out.println("-------*****getField******----------");
Field number = aClass.getField("NUMBER");
System.out.println(number);
System.out.println("-------*****getDeclaredFields******----------");
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField: declaredFields){
System.out.println(declaredField);
}
System.out.println("-------*******getDeclaredField****----------");
// 会抛出 NoSuchFieldException 异常 ,说明 getDeclaredField 无法获取到父类 中 由 private 修饰的field
Field declaredField = aClass.getDeclaredField("name");
System.out.println(declaredField);
}
}

其输出为:

1
2
3
4
5
6
7
8
9
10
11
------- ******getFields***** ----------
public static final java.lang.Integer mqray.learn.cn.service.entity.PersonEntity.NUMBER
-------*****getField******----------
public static final java.lang.Integer mqray.learn.cn.service.entity.PersonEntity.NUMBER
-------*****getDeclaredFields******----------
private static final java.lang.String mqray.learn.cn.service.entity.WriterEntity.Address
protected static final java.lang.String mqray.learn.cn.service.entity.WriterEntity.Province
-------*******getDeclaredField****----------
Exception in thread "main" java.lang.NoSuchFieldException: name
at java.base/java.lang.Class.getDeclaredField(Class.java:2412)
at reflect.TestReflectFieldWithParent.main(TestReflectFieldWithParent.java:26)

Method类及其用法

关于method的使用主要有如下几个方法:

1
2
3
4
1. getMethods() 获取所有由public 声明的方法
2. public Method getMethod(String name, Class<?>... parameterTypes) 指定方法名称和方法参数获取对应的public方法
3. getDeclaredMethods() 获取当前类所有声明方法,包括private和protected
4. public Method getDeclaredMethod(String name, Class<?>... parameterTypes) 指定方法名和参数获取被所有关键字声明的方法。

注意: 使用getDeclaredMethods获取所有声明的方法时,不能获取到父类的方法,只能获取到子类自己的方法。 而getMethods能获取到父类的方法。

无继承实现关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class TestReflectMethod {

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class<?> aClass = Class.forName("mqray.learn.cn.service.entity.PersonEntity");

Method[] methods = aClass.getMethods();
for(Method meth: methods){
System.out.println(meth);
}
System.out.println("-------***********----------");
Method method = aClass.getMethod("getName");
System.out.println(method);
System.out.println("-------***********----------");
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method method2: declaredMethods) {
System.out.println(method2);
}
System.out.println("-------***********----------");
Method getName = aClass.getDeclaredMethod("getName");
System.out.println(getName);

}
}

对应的输出如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public boolean mqray.learn.cn.service.entity.PersonEntity.equals(java.lang.Object)
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.toString()
public int mqray.learn.cn.service.entity.PersonEntity.hashCode()
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.getName()
public void mqray.learn.cn.service.entity.PersonEntity.setName(java.lang.String)
public void mqray.learn.cn.service.entity.PersonEntity.setGender(java.lang.String)
public java.lang.Double mqray.learn.cn.service.entity.PersonEntity.getHeight()
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.getInfo()
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.getGender()
public void mqray.learn.cn.service.entity.PersonEntity.setAge(int)
public void mqray.learn.cn.service.entity.PersonEntity.setOccupation(java.lang.String)
public int mqray.learn.cn.service.entity.PersonEntity.getAge()
public void mqray.learn.cn.service.entity.PersonEntity.setHeight(java.lang.Double)
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.getOccupation()
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
-------***********----------
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.getName()
-------***********----------
public boolean mqray.learn.cn.service.entity.PersonEntity.equals(java.lang.Object)
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.toString()
public int mqray.learn.cn.service.entity.PersonEntity.hashCode()
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.getName()
public void mqray.learn.cn.service.entity.PersonEntity.setName(java.lang.String)
public void mqray.learn.cn.service.entity.PersonEntity.setGender(java.lang.String)
public java.lang.Double mqray.learn.cn.service.entity.PersonEntity.getHeight()
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.getInfo()
private int mqray.learn.cn.service.entity.PersonEntity.getSalary()
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.getGender()
public void mqray.learn.cn.service.entity.PersonEntity.setAge(int)
public void mqray.learn.cn.service.entity.PersonEntity.setOccupation(java.lang.String)
protected boolean mqray.learn.cn.service.entity.PersonEntity.canEqual(java.lang.Object)
public int mqray.learn.cn.service.entity.PersonEntity.getAge()
public void mqray.learn.cn.service.entity.PersonEntity.setHeight(java.lang.Double)
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.getOccupation()
-------***********----------
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.getName()

从上述结果来看, getMethods会返回当前类包含被final关键字修饰的方法。而getDeclaredMethods则不会。

存在继承实现关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class TestReflectWithInterface {

public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class<?> aClass = Class.forName("mqray.learn.cn.service.entity.WriterEntity");

Method[] methods = aClass.getMethods();
for(Method meth: methods){
System.out.println(meth);
}
System.out.println("-------***********----------");
Method method = aClass.getMethod("getName");
System.out.println(method);
System.out.println("-------***********----------");
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method method2: declaredMethods) {
System.out.println(method2);
}
// 这里会抛出 NoSuchMethodException
// System.out.println("-------***********----------");
// Method getName = aClass.getDeclaredMethod("getName");
// System.out.println(getName);

System.out.println("-------***********----------");
Method walk = aClass.getDeclaredMethod("walk");
System.out.println(walk);
}
}

对应的输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public java.lang.String mqray.learn.cn.service.entity.WriterEntity.run()
public java.lang.String mqray.learn.cn.service.entity.WriterEntity.write()
public java.lang.String mqray.learn.cn.service.entity.WriterEntity.read()
public java.lang.String mqray.learn.cn.service.entity.WriterEntity.walk()
public boolean mqray.learn.cn.service.entity.PersonEntity.equals(java.lang.Object)
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.toString()
public int mqray.learn.cn.service.entity.PersonEntity.hashCode()
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.getName()
public void mqray.learn.cn.service.entity.PersonEntity.setName(java.lang.String)
public void mqray.learn.cn.service.entity.PersonEntity.setAge(int)
public void mqray.learn.cn.service.entity.PersonEntity.setOccupation(java.lang.String)
public int mqray.learn.cn.service.entity.PersonEntity.getAge()
public java.lang.Double mqray.learn.cn.service.entity.PersonEntity.getHeight()
public void mqray.learn.cn.service.entity.PersonEntity.setHeight(java.lang.Double)
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.getGender()
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.getInfo()
public void mqray.learn.cn.service.entity.PersonEntity.setGender(java.lang.String)
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.getOccupation()
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
-------***********----------
public java.lang.String mqray.learn.cn.service.entity.PersonEntity.getName()
-------***********----------
public java.lang.String mqray.learn.cn.service.entity.WriterEntity.run()
public java.lang.String mqray.learn.cn.service.entity.WriterEntity.write()
public java.lang.String mqray.learn.cn.service.entity.WriterEntity.read()
public java.lang.String mqray.learn.cn.service.entity.WriterEntity.walk()
-------***********----------
public java.lang.String mqray.learn.cn.service.entity.WriterEntity.walk()

这里值得注意的是,使用getDeclaredMethods获取所有声明的方法时,不能获取到父类的方法,只能获取到子类自己的方法。 而getMethods能获取到父类的方法。

反射机制的执行流程

反射执行流程

图片来源: Java 基础 - 反射机制详解

下面详细描述反射的执行过程:

1. 反射获取类实例

1
2
3
4
5
6
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}

这里的forName0由关键字native修饰,在对系统加载程序访问检查进行安全检查后由jvm调用。
这里的第二个参数ClassLoader.getClassLoader(caller)是获取当前类加载器,这部分在早先的文章中已经详细描述过,这里不再赘述。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
@CallerSensitive
@Deprecated(since="9")
public T newInstance()
throws InstantiationException, IllegalAccessException
{
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), false);
}

// NOTE: the following code may not be strictly correct under
// the current Java memory model.

// Constructor lookup
if (cachedConstructor == null) {
if (this == Class.class) {
throw new IllegalAccessException(
"Can not call newInstance() on the Class for java.lang.Class"
);
}
try {
Class<?>[] empty = {};
final Constructor<T> c = getReflectionFactory().copyConstructor(
getConstructor0(empty, Member.DECLARED));
// Disable accessibility checks on the constructor
// since we have to do the security check here anyway
// (the stack depth is wrong for the Constructor's
// security check to work)
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<>() {
public Void run() {
c.setAccessible(true);
return null;
}
});
cachedConstructor = c;
} catch (NoSuchMethodException e) {
throw (InstantiationException)
new InstantiationException(getName()).initCause(e);
}
}
Constructor<T> tmpConstructor = cachedConstructor;
// Security check (same as in java.lang.reflect.Constructor)
Class<?> caller = Reflection.getCallerClass();
if (newInstanceCallerCache != caller) {
int modifiers = tmpConstructor.getModifiers();
Reflection.ensureMemberAccess(caller, this, this, modifiers);
newInstanceCallerCache = caller;
}
// Run constructor
try {
return tmpConstructor.newInstance((Object[])null);
} catch (InvocationTargetException e) {
Unsafe.getUnsafe().throwException(e.getTargetException());
// Not reached
return null;
}
}
private transient volatile Constructor<T> cachedConstructor;
private transient volatile Class<?> newInstanceCallerCache;

接下来,获取类实例:
主体逻辑为:

  1. 调用checkMemberAccess校验权限
  2. 查找构造器
  3. 调用tmpConstructor.newInstance创建类实例
    其中第二步中会先判断cachedConstructor数组是否为空,如果为空,则调用
    final Constructor<T> c = getReflectionFactory().copyConstructor(getConstructor0(empty, Member.DECLARED));
    其中具体实现如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    private Constructor<T> getConstructor0(Class<?>[] parameterTypes,
    int which) throws NoSuchMethodException
    {
    ReflectionFactory fact = getReflectionFactory();
    Constructor<T>[] constructors = privateGetDeclaredConstructors((which == Member.PUBLIC));
    for (Constructor<T> constructor : constructors) {
    if (arrayContentsEq(parameterTypes,
    fact.getExecutableSharedParameterTypes(constructor))) {
    return constructor;
    }
    }
    throw new NoSuchMethodException(methodToString("<init>", parameterTypes));
    }
    上述代码中先获取ReflectionFactory,然后根据调用的构造器方法调用privateGetDeclaredConstructors方法,这里我们的入口是调用declaredConstructor。所以此处privateGetDeclaredConstructors的入参为false;
    这里拿到所有的构造器后,遍历获取参数符合预期的构造器,进行返回。

下面继续看 privateGetDeclaredConstructors的具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) {
Constructor<T>[] res;
ReflectionData<T> rd = reflectionData();
if (rd != null) {
res = publicOnly ? rd.publicConstructors : rd.declaredConstructors;
if (res != null) return res;
}
// No cached value available; request value from VM
if (isInterface()) {
@SuppressWarnings("unchecked")
Constructor<T>[] temporaryRes = (Constructor<T>[]) new Constructor<?>[0];
res = temporaryRes;
} else {
res = getDeclaredConstructors0(publicOnly);
}
if (rd != null) {
if (publicOnly) {
rd.publicConstructors = res;
} else {
rd.declaredConstructors = res;
}
}
return res;
}

上述过程中,先调用reflectionData获取反射信息,然后根据关键字声明参数,返回构造器。具体的,如果reflectionData中构造器不为空,则直接返回。
否则,根据是否是接口进行如下操作:

  1. 如果是接口,调用Constructor<T>[]) new Constructor<?>[0]返回构造器数组
  2. 否则调用getDeclaredConstructors0获取构造器数组,并存入缓存中。

先来看reflectionData()做了些什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Lazily create and cache ReflectionData
private ReflectionData<T> reflectionData() {
SoftReference<ReflectionData<T>> reflectionData = this.reflectionData;
int classRedefinedCount = this.classRedefinedCount;
ReflectionData<T> rd;
if (reflectionData != null &&
(rd = reflectionData.get()) != null &&
rd.redefinedCount == classRedefinedCount) {
return rd;
}
// else no SoftReference or cleared SoftReference or stale ReflectionData
// -> create and replace new instance
return newReflectionData(reflectionData, classRedefinedCount);
}

注意这里reflectionData的定义是:private transient volatile SoftReference<ReflectionData<T>> reflectionData;
SoftReference是软引用,被软引用指向的对象,当堆中内存不足的情况下,会被列入到可回收范围内,也就是说软引用指向的对象并非是安全的,在垃圾回收的过程中是有可能被回收掉的

再往下 就是调用原生方法private native Constructor<T>[] getDeclaredConstructors0(boolean publicOnly);

自此就完成了类实例的创建。

2. 反射获取方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@CallerSensitive
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
Objects.requireNonNull(name);
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkMemberAccess(sm, Member.PUBLIC, Reflection.getCallerClass(), true);
}
Method method = getMethod0(name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(methodToString(name, parameterTypes));
}
return getReflectionFactory().copyMethod(method);
}

同样的,先检查权限。
之后通过getMethod0获取指定名称和方法参数的Method

1
2
3
4
5
6
7
private Method getMethod0(String name, Class<?>[] parameterTypes) {
PublicMethods.MethodList res = getMethodsRecursive(
name,
parameterTypes == null ? EMPTY_CLASS_ARRAY : parameterTypes,
/* includeStatic */ true);
return res == null ? null : res.getMostSpecific();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private PublicMethods.MethodList getMethodsRecursive(String name,
Class<?>[] parameterTypes,
boolean includeStatic) {
// 1st check declared public methods
Method[] methods = privateGetDeclaredMethods(/* publicOnly */ true);
PublicMethods.MethodList res = PublicMethods.MethodList
.filter(methods, name, parameterTypes, includeStatic);
// if there is at least one match among declared methods, we need not
// search any further as such match surely overrides matching methods
// declared in superclass(es) or interface(s).
if (res != null) {
return res;
}

// if there was no match among declared methods,
// we must consult the superclass (if any) recursively...
Class<?> sc = getSuperclass();
if (sc != null) {
res = sc.getMethodsRecursive(name, parameterTypes, includeStatic);
}

// ...and coalesce the superclass methods with methods obtained
// from directly implemented interfaces excluding static methods...
for (Class<?> intf : getInterfaces(/* cloneArray */ false)) {
res = PublicMethods.MethodList.merge(
res, intf.getMethodsRecursive(name, parameterTypes,
/* includeStatic */ false));
}

return res;
}
  1. 先调用privateGetDeclaredMethods获取由 public关键字修饰的方法。
    privateGetDeclaredMethods的实现逻辑和之前获取构造器数组类似,都是先尝试调用reflectionData从缓存中获取。否则调用getDeclaredMethods0(publicOnly);从JVM中获取。紧接着调用Reflection.filterMethods过滤出最匹配的Method[].
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    private Method[] privateGetDeclaredMethods(boolean publicOnly) {
    Method[] res;
    ReflectionData<T> rd = reflectionData();
    if (rd != null) {
    res = publicOnly ? rd.declaredPublicMethods : rd.declaredMethods;
    if (res != null) return res;
    }
    // No cached value available; request value from VM
    res = Reflection.filterMethods(this, getDeclaredMethods0(publicOnly));
    if (rd != null) {
    if (publicOnly) {
    rd.declaredPublicMethods = res;
    } else {
    rd.declaredMethods = res;
    }
    }
    return res;
    }
  2. 尝试从父类的实现中获取方法
  3. 尝试从接口实现中获取指定方法

3. 调用method.invoke()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz,
Modifier.isStatic(modifiers) ? null : obj.getClass(),
modifiers);
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}

可以看到,最后是MethodAccessor调用invoke,如果当前方法中methodAccessor==null则调用acquireMethodAccessor获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private MethodAccessor acquireMethodAccessor() {
// First check to see if one has been created yet, and take it
// if so
MethodAccessor tmp = null;
if (root != null) tmp = root.getMethodAccessor();
if (tmp != null) {
methodAccessor = tmp;
} else {
// Otherwise fabricate one and propagate it up to the root
tmp = reflectionFactory.newMethodAccessor(this);
setMethodAccessor(tmp);
}

return tmp;
}

在获取MethodAccessor是都是先尝试从上下文缓存中获取,获取不到才从jvm中获取

4. 反射调用小结

  1. 反射类及反射方法的获取,都是通过从列表中搜寻查找匹配的方法,所以查找性能会随类的大小方法多少而变化;
  2. 每个类都会有一个与之对应的Class实例,从而每个类都可以获取method反射方法,并作用到其他实例身上;
  3. 反射也是考虑了线程安全的,放心使用;
  4. 反射使用软引用relectionData缓存class信息,避免每次重新从jvm获取带来的开销;
  5. 反射调用多次生成新代理Accessor, 而通过字节码生存的则考虑了卸载功能,所以会使用独立的类加载器;
  6. 当找到需要的方法,都会copy一份出来,而不是使用原来的实例,从而保证数据隔离;
  7. 调度反射方法,最终是由jvm执行invoke0()执行;

引用

1. Java 基础 - 反射机制详解