课件: Type.java为接口类 包:java.lang.reflect 实现类:Class.java TypeVariable: 泛型类型变量。可以泛型上下限等信息。 ParameterizedType: 具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)。 GenericArrayType: 当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。 WildcardType: 通配符泛型,获得上下限信息。
笔记:
// 1.获取Activity的class对象 Class<? extends Activity> clazz = activity.getClass(); // 2.判断当前Activity是否为InjectLayout修饰 if (clazz.isAnnotationPresent(InjectLayout.class)) { // 3.获取InjectLayout注解 InjectLayout annotation = clazz.getAnnotation(InjectLayout.class); if (annotation != null) { try { // 4.通过反射获取 当前Activity 的 setContentView 方法 Method method = clazz.getMethod("setContentView", int.class); // 5.获取注解上的value layoutId int resourceId = annotation.value(); // 6.调用setContentView method.invoke(activity, resourceId); //activity 为所需赋值的对象 } catch (Exception e) { e.printStackTrace(); } } }
反射创建实例
Constructor construtor = clz.getConstructor();
Object obj = construtor.newInstance();
动态代理 try { // 1.获取Activity的class对象 Class clazz = activity.getClass(); // 2.获取Activity的所有成员方法 排除继承方法 Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { // 3.是否是自定义注解修饰 if (method.isAnnotationPresent(OnClick.class)) {//TODO OnLongClick事件处理 OnClick onClick = method.getAnnotation(OnClick.class); int[] value = onClick.value(); // 4.获取注解上的注解 元注解 InjectEvent injectEvent = onClick.annotationType().getAnnotation(InjectEvent.class); String listenerSetter = injectEvent.listenerSetter(); Class listenerType = injectEvent.listenerType(); String methodName = injectEvent.methodName(); // 5.动态代理 生成代理的listener ProxyHandler handler=new ProxyHandler(activity); Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, handler); handler.mapMethod(methodName,method); // 6.反射获取 findViewById方法的Method对象 Method findViewByIdMethod = clazz.getMethod("findViewById", int.class); findViewByIdMethod.setAccessible(true); for (int id : value) { // 7.通过findViewById获取view View btn = (View) findViewByIdMethod.invoke(activity, id); // 8.根据listenerSetter方法名和listenerType方法参数找到method Method listenerSetMethod = btn.getClass().getMethod(listenerSetter, listenerType); listenerSetMethod.setAccessible(true); listenerSetMethod.invoke(btn, listener); } } }
总结:
1.反射几种:
1.通过类名实现反射机制
Class clz = Activity.class;
2.通过new对象实现反射机制
Class clz = activity.getClass();
3.通过路径实现反射机制
Class clazz = Class.forName("android.app.ActivityThread");
2.获取某个Class对象的方法集合,主要有以下几个方法:
//- getDeclaredMethods() 方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
//- getMethods() 方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。
//- getMethod方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象
3.如何设置方法、字段访问权限?
.setAccessible(true);
4.反射时,Filed set 或 Method invoke 时第一个参数为在哪个对象上去设置属性/调用方法。
如果时static方法是 传如null
method.invoke("实例对象", "参数");
method.invoke("null","参数");
如果method 为static方法时传入null。
5.通过getModifiers()函数判断是否方法/字段为static修饰
!Modifier.isPublic(cl.getModifiers())
面试题: 1.getMethod、getDeclaredMethods 区别? getDeclaredMethods() 方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。 getMethods() 方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。 getMethod方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象 2.如何设置方法、字段访问权限? .setAccessible(true); 3.为什么泛型擦出后,反射还能获取信息? {}匿名内部类 字节码中会记录泛型的签名信息 4.你觉的反射对性能有影响么?为什么? 类加载器访问一个类时,把类信息加载到JVM中 反射方法时,动态解析class将获取的方法中的数据组成Method对象 反射执行时,通过Method执行真正需要执行的方法。 整个过程中会产生额外的对象,而且执行方法时,相当于需要使用Method作为中间者来执行。会比直接执行方法慢。 5.反射对性能有损失,具体损失在哪里? 反射是基于程序集和元数据的,在使用反射的时候,会搜索元数据,而元数据是基于字符串的,并且无法预编译,所以这一系列操作对性能有影响。 大量的装箱拆箱也对性能有影响,由于我们对目标类型是位置的,而且向方法传递的参数通常是Object类型的,所以会有大量的装箱拆箱。 6.反射中getMethods与getDeclaredMethods的区别是什么? Method getMethod(String name,Class parameterType) 用于获取public的成员方法,包括从父类继承的 Method getDeclaredMethod(String name,Class parameterType) 用于获取当前类中的方法(不分public和非public) 7.反射中Class.forName()和ClassLoader.loadClass()的区别? 类加载流程 = 加载->验证->准备->解析->初始化->使用->卸载 Class.forName()方法,默认是需要初始化,一旦初始化,就会出发目标对象的static代码块执行 ClassLoader.loadClass(name)方法,不进行解析,意味着不进行包括初始化等一系列步骤,那么静态代码块和静态对象就不会得到执行。
反射与泛型会出现问题: //Exception in thread "main" java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType // at com.example.converge.note.androidbasics.inject.GsonTest$TypeReference.(Test.java:43) // at com.example.converge.note.androidbasics.inject.GsonTest.main(Test.java:75) static class TypeRefrence { Type type; public TypeReference() { //获得泛型类型 Type genericSuperclass = getClass().getGenericSuperclass(); ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass; //因为泛型可以定义多个A<T,E> 所以是个数组 Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); type = actualTypeArguments[0]; }
public Type getType() {
return type;
}
public void setType(Type type) {
this.type = type;
}
}
//加{}表示成匿名内部类,不加{}会报错,TypeToken<>会有泛型擦除操作,导致字节码中找不到Response相关签名信息。 Type type2 = new TypeToken<Response>(){}.getType(); System.out.println("type2 "+type2);
范型擦除后会返回Object类。 {}匿名内部类会在编译后,生成 static class ChildTypeReference<>{ Response t; }
也可以定义为抽象类,来限制必须加{}.
范型会在元数据(字节码)中记录范型信息。而元数据是基于字符串的,并且无法预编译,所以这一系列操作对性能有影响。所以反射对性能有损失。