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

news/2024/5/18 12:37:54 标签: java, android, 框架, 反射

一、Java反射机制


1、定义


        JAVA反射机制是在“运行状态”中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。Java反射机制主要提供了几个功能:在运行时判断任意一个对象所属的类、在运行时构造任意一个类的对象、在运行时判断任意一个类所具有的成员变量和方法、在运行时调用任意一个对象的方法。


2、获取Class对象


        当我们编译一个 Java 项目,所有的 Java 文件都会被编译成一个.class 文件,这些 class 文件在程序运行时会被 ClassLoader 加载到虚拟机中。当一个类被加载以后,Java 虚拟机就会在内存中自动产生一个 Class 对象。Java中,无论生成某个类的多少个对象,这些对象都会对应于同一个Class对象,这个Class对象是由JVM生成的,通过它能够获悉整个类的结构。我们通过 new 构造函数的形式创建对象实际上也是通过这些 Class 来创建。那么,我们在代码中如何获得Class对象呢?通常有三个方法,如下所示:

java">    /**
     * 获取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对象想去创建目标类对象,说是通过反射,实际上还是去执行类的构造函数。如下所示:

java">    /**
     * 反射获取类的对象
     *
     * @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) 来根据方法名获取某个方法。如下所示:

java">    /**
     * 反射获取类的方法
     */
    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) 来获取类的所有属性或单个属性。如下所示:

java">    /**
     * 反射获取类的属性
     */
    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对象获取父类或实现的接口


        如下所示:

java">    /**
     * 获取对象的父类
     */
    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来定义一个注解。如下所示:

java">@Documented                               // 是否保存到JavaDoc文档
@Retention(RetentionPolicy.RUNTIME)       // SOURCE(源码时),CLASS(编译时),RUNTIME(运行时),默认为 CLASS
@Target(ElementType.METHOD)               // 用于修饰哪些程序元素,如 TYPE, METHOD, CONSTRUCTOR, FIELD, PARAMETER 等,未标注则表示可修饰所有
@Inherited                                // 是否可以被继承,默认为 false
public @interface Test {

    int value() default 0;
}


        那么,我们如何通过注解来免去写findViewById(), setOnClickListener() ... ... 的麻烦呢?接下来我们就自定义一个简单的Android注解框架

        首先,定义一个注解InjectView,如下所示:

java">@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectView {

    int value() default 0;
}

        那么,我们要如何来解析这个注解呢?如下所示:

java">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了。如下所示:

java">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等等的注解。

java">    /**
     * 解析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


上一篇 下一篇

http://www.niftyadmin.cn/n/773909.html

相关文章

Java反射-Method

java的反射机制真的很强大&#xff0c;在这里总结了一些Method的基本应用。 import java.lang.reflect.Method; import java.util.Date;/*** ReflectMethod.java* 2017年11月8日*利用反射来调用方法并执行*/ class Person{public void doWork(){System.out.println("Pers…

暗黑字符串

题目描述 一个只包含’A’、’B’和’C’的字符串&#xff0c;如果存在某一段长度为3的连续子串中恰好’A’、’B’和’C’各有一个&#xff0c;那么这个字符串就是纯净的&#xff0c;否则这个字符串就是暗黑的。 例如&#xff1a; BAACAACCBAAA 连续子串”CBA”中包含了’A…

秋已小半

突如其来的寒冷才让我感觉秋天已经过快要结束了&#xff0c;我是一个特别畏惧寒冷的人&#xff0c;天气稍微有一点转凉&#xff0c;我的身体反应就特别明显&#xff0c;比如手脚冰凉。但是不知道为什么&#xff0c;我一直告诉自己也告诉所有问过我喜欢什么季节的人&#xff1a;…

Linux指令笔记-Vim编辑器

1. 普通模式 【h(或向左方向键)】 光标左移一个字符 【j(或向下方向键)】 光标下移一个字符 【k(或向上方向键)】 光标上移一个字符 【l(或向右方向键)】 光标右移一个字符 【[Ctrl] f】 屏幕向下移动一页&#xff08;相当于Page Down键&#xff09; 【[Ctrl] b】 屏幕…

面试题-构造队列

题目描述 小明同学把1到n这n个数字按照一定的顺序放入了一个队列Q中。现在他对队列Q执行了如下程序&#xff1a; while(!Q.empty()) //队列不空&#xff0c;执行循环 { int xQ.front(); //取出当前队头的值xQ.pop(); //弹出当前队头Q.push(x); …

java加载资源文件的两种方法

处理配置文件对于Java程序员来说再常见不过了&#xff0c;不管是Servlet&#xff0c;Spring&#xff0c;抑或是Structs&#xff0c;都需要与配置文件打交道。Java将配置文件当作一种资源(resource)来处理&#xff0c;并且提供了两个类来读取这些资源&#xff0c;一个是Class类&…

python基本数值类型简介

运算符 1、算数运算&#xff1a;2、比较运算&#xff1a; 3、赋值运算&#xff1a; 4、逻辑运算&#xff1a; 5、成员运算&#xff1a; - int&#xff08;整型&#xff09; 在32位机器上&#xff0c;整数的位数为32位&#xff0c;取值范围为-2**31&#xff5e;2**31-1&#xff…

python基本数据类型2

1.字符串 字符串可以使用下标 (索引); 就像 C 一样, 字符串的第一个字符的下标 (索引) 为 0. 索引可以是负数, 那样就会从右边开始算起.&#xff08;但是要注意, -0 与 0 是完全一样的, 因此它不会从右边开始数!&#xff09; 如果字符串里面有很多字符都需要转义&#xff0c;…