从Java反射机制到Android注解框架

来源:互联网 发布:淘宝旺旺引流神器 编辑:程序博客网 时间:2024/06/09 21:56

一、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


原创粉丝点击