我们知道,可以通过getDeclaredField和getField方法可以反射获取class中的元素,他们的区别在于getField只能访问公有变量,而getDeclaredField则可以访问定义的所有变量,包括protected修饰和private修饰的变量。
// 1.getDeclaredField方法调用searchFields方法
Field field = searchFields(privateGetDeclaredFields(false), name);
// 2.getField方法剪接调用searchFields方法
if ((res = searchFields(privateGetDeclaredFields(true), name)) != null) {
return res;
}
不过,若要访问父类中的元素,通过getDeclaredField和getField方法就会抛出NoSuchFieldException异常了。也就是说,无法直接用java.lang.Class中的方法去访问父类中的元素。Spring中的工具类org.springframework.util.ReflectionUtils提供了findField方法,能够取得父类中的元素。我们看一下其中的实现:
public static Field findField(Class<?> clazz, String name, Class<?> type) {
Assert.notNull(clazz, "Class must not be null");
Assert.isTrue(name != null || type != null, "Either name or type of the field must be specified");
Class<?> searchType = clazz;
while (!Object.class.equals(searchType) && searchType != null) {
// 这里一次性获取了类中的所有元素
Field[] fields = searchType.getDeclaredFields();
for (Field field : fields) {
if ((name == null || name.equals(field.getName())) && (type == null || type.equals(field.getType()))) {
return field;
}
}
// 获取所有父类,接着遍历父类中的元素。
searchType = searchType.getSuperclass();
}
return null;
}
笔者本希望通过getDeclaredField()方法直接反射获取class中的元素,不过为了获取其父类中的元素使用了工具类ReflectionUtils。我们通过计算getDeclaredField()方法和ReflectionUtils.findField()方法在1秒内获取类中元素的次数来判断两者的性能。
public static void main(String[] args) {
try {
int times = 0;
long startMili = System.currentTimeMillis();
while (true) {
long nowMili = System.currentTimeMillis();
InteBidResultDTO.class.getDeclaredField("balance");
times++;
if (nowMili > startMili + 1000) {
break;
}
}
System.out.println("getDeclaredField方法执行了" + times + "次");
times = 0;
startMili = System.currentTimeMillis();
while (true) {
long nowMili = System.currentTimeMillis();
ReflectionUtils.findField(InteBidResultDTO.class, "balance");
times++;
if (nowMili > startMili + 1000) {
break;
}
}
System.out.println("findField方法执行了" + times + "次");
} catch (SecurityException | NoSuchFieldException e) {
e.printStackTrace();
}
}
运行结果是:
getDeclaredField方法执行了3200996次
findField方法执行了2434921次
由于ReflectionUtils.findField()方法会去查找父类中的元素,性能必然会差一些。如果一个类的父类祖先越多,遍历次数也就越多,上述运行结果的差距也就更加明显。