反射API

作者: Parallel_Lines | 来源:发表于2018-08-18 14:10 被阅读0次

    本文将按如下导图讲述反射的使用

    Class对象的获取

    方法一:

    Class clazz = test.getClass();
    

    方法二:

    Class clazz = Test.class;
    

    方法三:

    try {
        Class clazz = Class.forName("com.app.xz.classtest.Test");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    

    一般使用第三种:

    1. 不依赖Java对象、不用引入包,只需要传入字符串即可获取Class对象,可灵活配置。
    2. 即使类加上了@hide,也能通过这种方式在java虚拟机中去寻找这个类有没有被加载。

    Class对象的使用

    通过Class对象获取类名

    class.getName();
    

    示例:

    Log.e(TAG, int.class.getName());
    Log.e(TAG, String.class.getName());
    Log.e(TAG, Test.class.getName());
    
    //数组
    int[] is = new int[3];
    Log.e(TAG, is.getClass().getName());
    
    String[] ss = new String[3];
    Log.e(TAG, ss.getClass().getName());
    
    Test[] ts = new Test[3];
    Log.e(TAG, ts.getClass().getName());
    
    //Logcat输出
    int
    java.lang.String
    com.app.xz.testclass.Test
    [I
    [Ljava.lang.String;
    [Lcom.app.xz.testclass.Test;
    

    注意点:

    1. 基本数据类型返回数据类型名,类返回包名加类名全路径。
    2. '['表示数组维度,如二维数组为'[['。
    3. '['后的大写字母表示当前数据类型关键字,映射关系如下所示:
    数据类型 关键字
    int I
    long J
    short S
    float F
    boolean Z
    byte B
    char C
    double D
    类、接口 L

    通过Class对象获取类的修饰符

    int modifiers = clazz.getModifiers();
    Modifier.toString(modifiers);
    Modifier.isPublic(modifiers);
    
    //Modifier.toString的输出结果如:
    public abstract
    

    通过Class对象获取类的成员变量Field

    获取指定名字的Field

    public Field getDeclaredField(String name)
                           throws NoSuchFieldException,
                                  SecurityException;
    
    public Field getField(String name)
                   throws NoSuchFieldException,
                          SecurityException
    

    获取所有Field

    public Field[] getDeclaredFields() throws SecurityException {}
    
    public Field[] getFields() throws SecurityException {
    

    下面通过测试代码解析getFieldsgetDeclaredFields的区别

    测试代码1:

    
    //Bean类
    public class Child {
    
        int a = 0;
    
        private int b = 1;
    
        protected int c = 2;
    
        public int d = 3;
    }
    
    //测试代码
    Class clazz = Child.class;
    
    for (Field field : clazz.getFields()) {
        Log.e(TAG, field.getName());
    }
            
    for (Field field : clazz.getDeclaredFields()) {
        Log.e(TAG, field.getName());
    }
    
    //getFields输出
    d
    
    //getDeclaredFields输出
    a
    b
    c
    d
    

    测试代码2:

    
    //Bean类
    public class Child extends Father{}
    
    public class Father {
    
        int a = 0;
    
        private int b = 1;
    
        protected int c = 2;
    
        public int d = 3;
    }
    
    //测试代码
    Class clazz = Child.class;
    
    for (Field field : clazz.getFields()) {
        Log.e(TAG, field.getName());
    }
            
    for (Field field : clazz.getDeclaredFields()) {
        Log.e(TAG, field.getName());
    }
    
    //getFields输出
    d
    
    //getDeclaredFields无输出
    

    结论:

    getFields获得所有 public 修饰的成员变量,包括父类。
    getDeclaredFields获得所有成员变量,但不包括父类。

    下面是Field的用法。

    通过Field获取成员变量的类型

    public Type getGenericType() {}
    
    public Class<?> getType() {}
    

    区别参照测试代码

    //Bean类
    public class Child<T> {
    
        public int a;
    
        public T data;
        
        public List<String> strings;
    
        public Test test;
    }
    
    //测试代码
    for (Field field : clazz.getDeclaredFields()) {
        Log.e(TAG, field.getName());
        Log.e(TAG, field.getGenericType().toString());
        Log.e(TAG, field.getType().getName());
        Log.e(TAG, "");
    }
    
    //输出结果
    a
    int
    int
      
    data
    T
    java.lang.Object
     
    strings
    java.util.List<java.lang.String>
    java.util.List
    
    test
    class com.app.xz.testclass.Test
    com.app.xz.testclass.Test
    

    通过Field获取成员变量的修饰符

    同class

    public int getModifiers() {}
    

    通过Field对成员变量执行get、set

    Field是类的抽象成员变量,不涉及对象。
    如果要对某对象的成员变量进行读取和赋值,需要传入对象。

    如'鸟'类有'羽毛'这个成员变量,则'羽毛'代表一个Field。对于鸟A,'羽毛'这个Field是白色;对于鸟B,'羽毛'这个Field是花色。即Field代表类中的一个成员变量,它的取值和赋值需要对象才有意义。

    get

    public Object get(Object obj);
    
    public int getInt(Object obj);
    
    public long getLong(Object obj)
            throws IllegalArgumentException, IllegalAccessException;
    
    public float getFloat(Object obj)
            throws IllegalArgumentException, IllegalAccessException;
    
    public short getShort(Object obj)
            throws IllegalArgumentException, IllegalAccessException;
    
    public double getDouble(Object obj)
            throws IllegalArgumentException, IllegalAccessException;
    
    public char getChar(Object obj)
            throws IllegalArgumentException, IllegalAccessException;
    
    public byte getByte(Object obj)
            throws IllegalArgumentException, IllegalAccessException;
    
    public boolean getBoolean(Object obj)
            throws IllegalArgumentException, IllegalAccessException
    

    set

    public void set(Object obj, Object value);
    
    public void setInt(Object obj,int value);
    
    public void setLong(Object obj,long value)
            throws IllegalArgumentException, IllegalAccessException;
    
    public void setFloat(Object obj,float value)
            throws IllegalArgumentException, IllegalAccessException;
    
    public void setShort(Object obj,short value)
            throws IllegalArgumentException, IllegalAccessException;
    
    public void setDouble(Object obj,double value)
            throws IllegalArgumentException, IllegalAccessException;
    
    public void setChar(Object obj,char value)
            throws IllegalArgumentException, IllegalAccessException;
    
    public void setByte(Object obj,byte b)
            throws IllegalArgumentException, IllegalAccessException;
    
    public void setBoolean(Object obj,boolean b)
            throws IllegalArgumentException, IllegalAccessException
    

    示例代码:

    try {
        Field f = Bird.class.getDeclaredField("feather");
        //私有的成员变量,需要通过下面代码赋予操作权限
        f.setAccessible(true);
        //传入test对象以获取该对象的car成员变量
        Feather feather = (Feather) f.get(bird);
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
    

    通过Class对象获取类的方法Method

    public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
    
    public Method getMethod(String name, Class<?>... parameterTypes)
    
    public Method[] getDeclaredMethods() throws SecurityException
    
    public Method getMethod(String name, Class<?>... parameterTypes) 
    

    区别同上,getMethod获得包括父类在内的所有 public 修饰的方法,getDeclaredMethods获得所有方法,但不包括父类。

    示例代码:

    //Bean类
    public class Child {
    
        void sayDefault() {
        }
    
        private void sayPrivate() {
        }
    
        protected void sayProtect() {
        }
    
        public void sayPublic() {
        }
    }
    
    //测试代码
    Class clazz = Child.class;
    
    for (Method method : clazz.getMethods()) {
        Log.e(TAG, method.getName());
    }
    
    for (Method method : clazz.getDeclaredMethods()) {
        Log.e(TAG, method.getName());
    }
    
    //getMethods输出结果
    equals
    getClass
    hashCode
    notify
    notifyAll
    sayPublic
    toString
    wait
    wait
    
    //getDeclaredMethods输出结果
    sayPrivate
    sayDefault
    sayProtect
    sayPublic
    

    通过Method获取方法名

    getName();
    

    通过Method获取方法参数

    public Parameter[] getParameters() {}
    
    public Class<?>[] getParameterTypes() {}
    
    public Type[] getGenericParameterTypes() {}
    

    其中Paramter还可以获得参数的名字和修饰符:

    // 获取参数名字
    public String getName() {}
    
    // 获取参数类型
    public Class<?> getType() {}
    
    // 获取参数的修饰符
    public int getModifiers() {}
    

    使用视情况而定。

    通过Method获取方法返回值类型

    // 获取返回值类型
    public Class<?> getReturnType() {}
    
    
    // 获取返回值类型包括泛型
    public Type getGenericReturnType() {}
    

    通过Method获取方法异常类型

    public Class<?>[] getExceptionTypes() {}
    
    public Type[] getGenericExceptionTypes() {}
    

    通过Method获取方法修饰符

    public int getModifiers() {}
    

    通过Method执行方法

    public Object invoke(Object obj, Object... args) {}
    

    要点:

    1. invoke() 方法中第一个参数 Object 实质上是 Method 所依附的 Class 对应的类的实例,如果这个方法是一个静态方法,那么 ojb 为 null,后面的可变参数 Object 对应的自然就是参数。

    2. invoke() 返回的对象是 Object,所以实际上执行的时候要进行强制转换。

    3. 在对 Method 调用 invoke() 的时候,如果方法本身会抛出异常,那么这个异常就会经过包装,由 Method 统一抛出 InvocationTargetException。而通过 InvocationTargetException.getCause() 可以获取真正的异常。

    示例代码:

    //Bean类
    public class Child {
    
        private static void say(Context context, String text) {
    
            Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
        }
    }
    
    //测试代码
    Class clazz = Child.class;
    
    try {
        Method say = clazz.getDeclaredMethod("say", Context.class, String.class);
        say.setAccessible(true);
        say.invoke(null, this, "six!");
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    

    通过反射的方式,可以调用Android一些私有的方法。

    通过Class对象获取构造函数Constructor

    参数为Class,表示构造函数参数的类型,无参可不传。

    //获取指定构造函数
    Constructor<T> constructor = class.getConstructor(Class<?>...params);
    
    Constructor<T> constructor = class.getDeclaredConstructor(Class<?>...params);
    
    //获取所有构造函数
    Constructor<?>[] constructors = class.getConstructors();
    
    Constructor<?>[] constructors = class.getDeclaredConstructors();
    

    区别是:

    getDeclaredConstructors可以获取所有构造函数,但是不包括父类。
    getConstructors可以获得 public 修饰的所有构造函数,也不包括父类。

    这里和Field、Method有所区别,Field、Method是可以获取到父类的 public 修饰的变量和方法的。
    仔细想想:子类本来就可以调用父类的非私有成员变量和方法,但子类不能利用父类的构造函数初始化自己,反射也是基于此。

    通过Constructor生成对象实例

    获取对象有俩种方式:

    方法一:利用class

    class.newInstance();
    

    方法二:利用constructor

    constructor.setAccessible(true);
    constructor.newInstance(Object...params);
    

    区别是:

    class.newInstance()仅能通过非private无参构造函数生成对象。
    Constructor上面说过,通过不同API,可以获取所有的构造函数,并通过构造函数生成对象。

    通过Constructor获取构造函数参数类型

    public Parameter[] getParameters() {}
    
    public Class<?>[] getParameterTypes() {}
    
    public Type[] getGenericParameterTypes() {}
    

    通过Constructor获取构造函数异常类型

    public Class<?>[] getExceptionTypes() {}
    
    public Type[] getGenericExceptionTypes() {}
    

    通过Constructor获取构造函数修饰符

    public int getModifiers() {}
    

    TOC

    [TOC]

    相关文章

      网友评论

          本文标题:反射API

          本文链接:https://www.haomeiwen.com/subject/tirfiftx.html