反射API

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

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]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容