反射API

2018-08-18  本文已影响0人  Parallel_Lines

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

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]

上一篇下一篇

猜你喜欢

热点阅读