一、Java反射机制
1、定义
JAVA反射机制是在“运行状态”中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。Java反射机制主要提供了几个功能:在运行时判断任意一个对象所属的类、在运行时构造任意一个类的对象、在运行时判断任意一个类所具有的成员变量和方法、在运行时调用任意一个对象的方法。
2、获取Class对象
当我们编译一个 Java 项目,所有的 Java 文件都会被编译成一个.class 文件,这些 class 文件在程序运行时会被 ClassLoader 加载到虚拟机中。当一个类被加载以后,Java 虚拟机就会在内存中自动产生一个 Class 对象。Java中,无论生成某个类的多少个对象,这些对象都会对应于同一个Class对象,这个Class对象是由JVM生成的,通过它能够获悉整个类的结构。我们通过 new 构造函数的形式创建对象实际上也是通过这些 Class 来创建。那么,我们在代码中如何获得Class对象呢?通常有三个方法,如下所示:
/** * 获取Class对象的三种方式 */ public static Class<?> getClassObj() { // 根据类名获取Class对象 Class<?> clazz1 = People.class; // 根据对象获取Class对象 People people = new People(); Class<?> clazz2 = people.getClass(); // 根据完整类名获取Class对象 try { Class<?> clazz3 = Class.forName("com.yuyh.reflection.java.People"); } catch (ClassNotFoundException e) { Log.e(TAG, e.toString()); } Log.i(TAG, "clazz1 = " + clazz1); return clazz1; // clazz2 clazz3 }
3、通过Class对象获取目标类的对象
平时所熟悉的创建对象的方式就是去new一个类,执行他们的构造函数,那么当我们拿到Class对象想去创建目标类对象,说是通过反射,实际上还是去执行类的构造函数。如下所示:
/** * 反射获取类的对象 * * @return */ public static Object getObject() { try { // 获取类的Class对象 Class<?> clz = getClassObj(); // 获取类对象的Constructor Constructor<?> constructor = clz.getConstructor(String.class, int.class, String.class); // 在使用时取消 Java语言访问检查,提升反射性能 constructor.setAccessible(true); // 通过 Constructor 来创建对象 Object obj = constructor.newInstance("yuyh", 25, "xxx@gmail.com"); Log.i(TAG, obj.toString()); return obj; } catch (Exception e) { Log.e(TAG, e.toString()); } return null; }
4、通过Class对象获取类的所有方法
同样,拿到Class对象之后我们可以通过调用getDeclaredMethods或getMethods(包括从父类继承下来的方法) 去获取类的所有方法,也可调用getDeclaredMethod (String name, Class...<?> parameterTypes) 来根据方法名获取某个方法。如下所示:
/** * 反射获取类的方法 */ public static void getDeclaredMethods() { People people = (People) getObject(); // 获取到类中的所有方法(不包含从父类继承的方法) Method[] methods = people.getClass().getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { Log.i(TAG, "method[" + i + "] = " + methods[i].getName()); } try { // 获取类中的某个方法 Method method = people.getClass().getDeclaredMethod("setEMail", String.class); // 判断是否是public方法 Log.i(TAG, "method is public = " + Modifier.isProtected(method.getModifiers())); // 获取该方法的参数类型列表 Class<?>[] paramTypes = method.getParameterTypes(); for (int i = 0; i < paramTypes.length; i++) { Log.i(TAG, "paramTypes[" + i + "] = " + paramTypes[i].getName()); } Log.i(TAG, "people.email befor= " + people.getEMail()); // 执行该方法 method.invoke(people, "xxx@163.com"); Log.i(TAG, "people.email after= " + people.getEMail()); } catch (Exception e) { Log.e(TAG, e.toString()); } }
5、通过Class对象获取类的所有属性
同理,可通过getDeclaredFields、 getFields、 getDeclaredField (String name)、 getField (String name) 来获取类的所有属性或单个属性。如下所示:
/** * 反射获取类的属性 */ public static void getDeclaredFields() { People people = (People) getObject(); // 获取当前类所有属性 Field[] fields = people.getClass().getDeclaredFields(); for (int i = 0; i < fields.length; i++) { Log.i(TAG, "fields[" + i + "] = " + fields[i].getName()); } try { // 获取当前类的某个属性 Field field = people.getClass().getDeclaredField("name"); // 获取属性值 Log.i(TAG, "people.name before = " + field.get(people)); // 设置属性值 field.set(people, "yuyh1"); Log.i(TAG, "people.name after = " + field.get(people)); } catch (Exception e) { e.printStackTrace(); } }
6、根据Class对象获取父类或实现的接口
如下所示:
/** * 获取对象的父类 */ public static void getSuperClass() { Student student = new Student("142315079"); Class<?> superClass = student.getClass().getSuperclass(); while (superClass != null) { Log.i(TAG, "superClass = " + superClass.getName()); superClass = superClass.getSuperclass(); // 循环获取上一层父类(如果存在),至少存在一层java.lang.Object } } /** * 获取对象实现的接口 */ public static void getInterface() { Student student = new Student("142315079"); // 获取该类实现的所有接口 Class<?>[] interfaces = student.getClass().getInterfaces(); for (int i = 0; i < interfaces.length; i++) { Log.i(TAG, "interfaces[" + i + "] = " + interfaces[i].getName()); } }
二、从Java反射到Android注解
俗话说,不会偷懒的程序员不是好程序员。相信初学Android的时候,大家都会被一堆findViewById()并且还要进行强转这种简单没营养,又不得不写的代码气死,显然,Android注解也在一定程度上帮助了你成为一名偷懒的程序猿。
什么是注解?通常我们可以把它理解为一个标记,最常见的注解有:@Override,@Deprecated,@SuppressWarnings等。注解本质上也是一个类,他不是通过class和interface来定义,而是通过@interface来定义一个注解。如下所示:
@Documented // 是否保存到JavaDoc文档@Retention(RetentionPolicy.RUNTIME) // SOURCE(源码时),CLASS(编译时),RUNTIME(运行时),默认为 CLASS@Target(ElementType.METHOD) // 用于修饰哪些程序元素,如 TYPE, METHOD, CONSTRUCTOR, FIELD, PARAMETER 等,未标注则表示可修饰所有@Inherited // 是否可以被继承,默认为 falsepublic @interface Test { int value() default 0;}
那么,我们如何通过注解来免去写findViewById(), setOnClickListener() ... ... 的麻烦呢?接下来我们就自定义一个简单的Android注解框架。
首先,定义一个注解InjectView,如下所示:
@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface InjectView { int value() default 0;}
那么,我们要如何来解析这个注解呢?如下所示:
package com.yuyh.reflection.annotation;import android.app.Activity;import android.util.Log;import java.lang.reflect.Field;/** * @author yuyh. * @date 2016/6/13. */public class Inject { public static final String TAG = "Reflection"; public static void inject(Activity activity) { getAnnotationInfos(activity); } private static void getAnnotationInfos(Activity activity) { Class clazz = activity.getClass(); Log.i(TAG, clazz.getName()); Field[] fields = clazz.getFields(); for (Field field : fields) { InjectView injectView = field.getAnnotation(InjectView.class); if (injectView != null) { int id = injectView.value(); try { field.setAccessible(true); field.set(activity, activity.findViewById(id)); } catch (IllegalAccessException e) { Log.e(TAG, "IllegalAccessException = " + e.toString()); } } } }}
那么,在Activity中就可以进行注解View了。如下所示:
public class MainActivity extends AppCompatActivity { @InjectView(R.id.hello) TextView tvHello; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Inject.inject(this); // 初始化注解 tvHello.setText("hahaha"); }}
tvHello.setText()方法没有报空指针,并且成功设置值,说明我们注解View实现成功。 同理,我们还可对实现对setOnClickListener等等的注解。
/** * 解析OnClick以及OnLongClick注解 * * @param activity */ private static void injectClick(final Activity activity) { Class clazz = activity.getClass(); Log.i(TAG, clazz.getName()); Method[] methods = clazz.getDeclaredMethods(); for (final Method method : methods) { OnClick click = method.getAnnotation(OnClick.class); OnLongClick longClick = method.getAnnotation(OnLongClick.class); if (click != null && click.value() != 0) { View view = activity.findViewById(click.value());//通过注解的值获取View控件 if (view == null) return; view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { method.invoke(activity, v);//通过反射来调用被注解修饰的方法,把View传回去 } catch (InvocationTargetException e) { Log.e(TAG, "InvocationTargetException = " + e.toString()); } catch (IllegalAccessException e) { Log.e(TAG, "IllegalAccessException = " + e.toString()); } } }); } if (longClick != null && longClick.value() != 0) { View view = activity.findViewById(click.value()); if (view == null) return; view.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { try { method.invoke(activity, v); } catch (InvocationTargetException e) { Log.e(TAG, "InvocationTargetException = " + e.toString()); } catch (IllegalAccessException e) { Log.e(TAG, "IllegalAccessException = " + e.toString()); } return true; } }); } } }
源码地址:https://github.com/smuyyh/ReflectionDemo
Thank you for reading~~
文章转自yyh352091626:http://m.blog.csdn.net/yyh352091626/article/details/51657908?utm_source=qq&utm_medium=social