作用
通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性,有以下特点:
- 运行时获取,较为灵活
- 几乎所有框架都使用了反射机制
- 破坏了封装性,因为反射可以获得类的所有属性与方法
可以将反射当成一个 Java 的 API,就像 HashMap、Unsafe 等等类似
用法
如何获取类
1,Class.forName 方法接类的详细地址
2,类名 .class
3,对象名 .getClass
4,类加载器 ClassLoader.loadClass 接类的详细地址
比如以下代码,我们使用 .class 获取了一个对象的 class 对象
Class<WeChatCorpMessage> messageClass = WeChatCorpMessage.class;
获取类之后我们可以使用 newInstance 方法获取对象
/**
* 获取 TargetObject 类的 Class 对象并且创建 TargetObject 类实例
*/
TargetObject targetObject = (TargetObject) targetClass.newInstance();
获取 class 对象后,可以接着获取以下的对象,来对数据做更多操作
获取 Method
Method:获取对象的方法
/**
* 获取 TargetObject 类中定义的所有方法
*/
Method[] methods = targetClass.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
// 判断 Index 类型的注解是否在该属性上
if (field.isAnnotationPresent(Index.class)) {
// 获取这个注解
Annotation p = field.getAnnotation(Index.class);
}
}
/**
* 获取指定方法
*/
Method publicMethod = targetClass.getDeclaredMethod("publicMethod",
String.class);
/**
* 调用指定方法,第一个参数为调用该方法的对象,其他参数为该方法的入参
*/
publicMethod.invoke(targetObject, "Hello World");
/**
* 调用 private 方法
*/
Method privateMethod = targetClass.getDeclaredMethod("privateMethod");
//为了调用 private 方法我们取消安全检查
privateMethod.setAccessible(true);
privateMethod.invoke(targetObject);
获取 Field
Field:获取对象的属性
/**
* 获取所有参数
*/
Field[] fields = targetObject.getDeclaredFields();
/**
* 强行获取指定参数并对参数进行修改(getField 非强行获取)
*/
Field field = targetClass.getDeclaredField("value");
// 为了对类中的参数进行修改我们取消安全检查(破坏封装性)
field.setAccessible(true);
// 相当于 targetObject.setValue("Hello World")
field.set(targetObject, "Hello World");
// 获取该属性的值
(String) field.get(targetObject);
// 获取属性名
field.getName();
// 查询该属性上是否被某个注解修饰
field.isAnnotationPresent(CheckSensitive.class);
// 获取该属性上的这个注解
field.getAnnotation(CheckSensitive.class);
这里额外说明一下,程序中不要破封装。如果需要通过类的属性获取操作属性的方法,可以这么写:
try {
PropertyDescriptor pd = new PropertyDescriptor(declaredField.getName(), aClass);
Method readMethod = pd.getReadMethod();
Object invoke = readMethod.invoke(o);
} catch (Exception exception) {
}
反射对性能的影响
反射的确会导致性能问题; 反射导致的性能问题是否严重跟使用的次数有关系,如果控制在100次以内,基本上没什么差别,如果调用次数超过了100次,性能差异会很明显
原因如下:
1,反射需要检查方法可见性,反射时每次调用都必须检查方法的可见性(在 Method.invoke 里),也就是做一些所谓的安全认证,比如这个类能不能访问,这个方法能不能调用之类的
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}
2,需要校验参数:反射时必须检查每个实际参数与形式参数的类型匹配性(在NativeMethodAccessorImpl.invoke0 里或者生成的 Java 版 MethodAccessor.invoke 里)
3, JIT 无法优化:反射涉及到动态加载的类型,JIT 无法进行优化。
动态加载(Dynamic Loading)是指在程序运行时根据需要动态地加载类或模块,而不是在编译时就确定加载的类或模块。例如,编写一个面向接口的应用程序,可以等到运行时再指定其实际的实现类,用户可以通过 Java 预置的或自定义类加载器,让某个本地的应用程序在运行时从网络或其他地方上加载一个二进制流作为其程序代码的一部分
JIT 是 just in time 的缩写, 也就是即时编译编译器。在运行时 JIT 会把翻译过的机器码保存起来,以备下次使用