Android反射

注意:本篇文章是本人阅读相关文章所写下的总结,方便以后查阅,所有内容非原创,侵权删。

本篇文章内容来自于
夯实JAVA基本之二 —— 反射(1):基本类周边信息获取
夯实JAVA基本之二 —— 反射(3):类内部信息获取

前言

在应用程序开发时,如果纯做上层,搭搭框架啥的,那用到反射的机会不多。
但如果你想做出来一个公共类或者公共模块给其它人用的时候,那用到反射的可能性就大大增加了。

读完这篇文章应该知道:

了解JVM的加载机制知道同一个类只会被类装载器装载一次
通过不同的方法得到的Class对象都是同一个。Class对象可类也可接口。
而Class对象的Field,Method的对象对应的不是哪个类实例中变量,而是类中变量的对象,是在编译时保存在Class中的同一个
而Class的实例却可以有很多个,如果要改变哪个Person实例中成员变量的值,就必须将该Person实例传入Field对象的参数中,让Field对象设置。
反射可以应用在“方法参数只需传入Class名,方法中通过反射可以使用这个Class”。

目录

  1. JVM的加载机制
    --1.1 编译(阶段一)
    --1.2 运行(阶段二)
    ----1.2.1 装载
    ----1.2.2 链接
    ----1.2.3 初始化
  2. 获取类类型(Class对象可类也可接口)
  3. 类周边信息获取(基本类和泛型类)【泛型类相关的待补】
    --3.1 类名、包名获取
    --3.2 获取超类Class对象
    --3.3 获取类所直接继承的接口的Class对象
    --3.4 获取类的访问修饰符
  4. 类内部信息获取【泛型类相关的待补】
    --4.1 构造函数相关获取 Constructor
    ----4.1.1 获取类的构造函数
    ----4.1.2 使用获取到的构造函数Constructor构造实例
    ----4.1.3 得到获取到的构造函数Constructor的参数
    ----4.1.4 得到获取到的构造函数Constructor的访问修饰符
    ----4.1.5 得到声明Constructor的类的Class对象
    --4.2 成员变量的获取与设置 Field
    ----4.2.1 获取Field对象
    ----4.2.2 Field的get、set操作
    ----4.2.3 Field之isEnumConstant()
    ----4.2.4 Field之Modifiers与getDeclaringClass()
    --5.3 成员函数的获取与设置 Method
    ----5.3.1 获取Method对象
    ----5.3.2 Method之Invoke
    ----5.3.3 Method之获取参数类型
    ----5.3.4 Method之获取返回值类型

1. JVM的加载机制

查看JVM的加载机制

2. 获取类类型

类只会被装载一次,利用装载的类可以实例化出各种不同的对象。
而反射就是通过获取装载的类来做出各种操作的。
装载的类,我们称为类类型,利用装载的类产生的实例,我们称为类实例

//方法一:通过类实例的getClass()方法得到类类型。
Person person = new Person();    
Class a = person.getClass()   
//方法二:直接通过类的class对象得到  
Class b = Persion.class;   //等同于Class class1 = Persion.class

//方法三:通过类名得到(ClassName一定要从包名具体到类名) 
//不仅会将类加载进来,而且会对其进行初始化
Class c = Class.forName(String ClassName);   
//方法四:(不建议使用)通过类名得到(ClassName一定要从包名具体到类名) 
//只是将类加载进来,而没有对类进行初始化。
Class d = context.getClassLoader().loadClass(String ClassName);  
//如果你加载类依赖初始化值的话,那ClassLoader.loadClass(String ClassName)将不再适用。

类只会被加载一次,所以a,b,c,d都是相等的,因为他们都是指向同一个对象。

Class对象,它可能表示的类对象,也可能表示的是接口对象!

3. 类周边信息获取

类分为基本类和泛型类
Class对象,它可能表示的类对象,也可能表示的是接口对象!

3.1 类名、包名获取

相关函数

//获取完整的类名(包含包名)  
public String getName();  
//仅获取类名  
public String getSimpleName();  
//获取类类型所对应的package对象  
public Package getPackage()  

使用

Class class1 = Animal.class;  
Package package1 = class1.getPackage(); 
  
Log.d(TAG,"完整的类名:"+class1.getName());  
Log.d(TAG,"仅获取类名:"+class1.getSimpleName());  
Log.d(TAG,"包名:"+package1.getName());  

3.2 获取超类Class对象

相关函数

//获取普通函数的父类Class对象  
public Class getSuperclass();  
//针对泛型父类而设计  
public Type getGenericSuperclass(); 

使用

Class class2 = Class.forName("com.example.myReflect.AnimalImpl");  
Class parentClass = class2.getSuperclass();  

3.3 获取类所直接继承的接口的Class对象

Class类,不同于定义类的class标识,Class类是一个泛型。
类对象是由Class对象来表示,而接口对象同样也是用Class对象来表示!
所以同样是Class对象,它可能表示的类对象,也可能表示的是接口对象!

相关函数

//获取普通接口的方法  
public Class[] getInterfaces();  
//获取泛型接口的方法  
public Type[] getGenericInterfaces();  

使用

Class class3 = Animal.class;  
Class[] interfaces = class3.getInterfaces();  
for (Class interItem:interfaces){  
    Log.d(TAG, "Animal继承的接口:" + interItem.getName());  
}  

3.4 获取类的访问修饰符

什么是访问修饰符?

//前面的那一坨public static final,就是类的访问修饰符,
//是定义这个类在的访问区域和访问限定的。
public static final class InnerClass{  
}

获取实例

//可以为接口也可以为类
Class clazz = getClassLoader().loadClass(InnerClass.class.getName());  

//通过clazz.getModifiers()得到一个整型变量,
//由于访问修饰符有很多,所以这些修饰符被打包成一个int,
//对应的二进制中,每个修饰符是一个标志位,可以被置位或清零。
//且单独提供了一个类Modifier来提取这个整型变量中各标识位的函数
int modifiers = clazz.getModifiers();  

//Modifier提供了相应的方法来获取修饰符
String retval = Modifier.toString(modifiers);  
boolean isFinal = Modifier.isFinal(modifiers);  
Log.d(TAG, "InnerClass的定义修饰符:" + retval);  
Log.d(TAG, "is Final:" + isFinal);  

Modifier中主要有以下几个方法

//根据整型变量来生成对应的修饰符字符串  
//这个函数的作用就是根据传进来的整型,根据其中的标识位来判断具有哪个修饰符,然后将所有修饰符拼接起来输出。
String Modifier.toString(int modifiers)   
//以下这些方法来检查特定的修饰符是否存在  
boolean Modifier.isAbstract(int modifiers)  
boolean Modifier.isFinal(int modifiers)  
boolean Modifier.isInterface(int modifiers)  
boolean Modifier.isNative(int modifiers)  
boolean Modifier.isPrivate(int modifiers)  
boolean Modifier.isProtected(int modifiers)  
boolean Modifier.isPublic(int modifiers)  
boolean Modifier.isStatic(int modifiers)  
boolean Modifier.isStrict(int modifiers)  
boolean Modifier.isSynchronized(int modifiers)  
boolean Modifier.isTransient(int modifiers)  
boolean Modifier.isVolatile(int modifiers) 

4. 类内部信息获取

Field,Method的对象对应的不是哪个类实例中变量,而是类中变量的对象。
也就是说,Field,Method对象只有一个,是在编译时,保存在Class中的。
而Person的实例却可以有很多个,如果要改变哪个Person实例中成员变量的值,就必须将该Person实例传入Field对象的参数中,让Field对象设置。

4.1 构造函数相关获取 Constructor

4.1.1 获取类的构造函数

相关方法

//获取public类型的构造函数  
Constructor[] getConstructors();  
Constructor getConstructor(Class... parameterTypes);  
  
//获取所有类型的构造函数  
Constructor[] getDeclaredConstructors();  
Constructor getDeclaredConstructor(Class... parameterTypes) 

使用实例
以getDeclaredConstructors()为例

//Constructor[] getDeclaredConstructors();  
//枚举
Class clazz = Person.class;  
Constructor[] constructors = clazz.getDeclaredConstructors();  
for (Constructor item:constructors){  
    Log.d(TAG,"枚举到的构造函数:"+item.toString());  
}  
  
//Constructor getDeclaredConstructor(Class... parameterTypes)
//根据类型,获取指定的构造的构造函数  
Constructor constructor = clazz.getDeclaredConstructor(Integer.class, String.class);  
Log.d(TAG, "指定参数得到的构造函数:"+constructor.toString());  
4.1.2 使用获取到的构造函数Constructor构造实例

相关函数

//注意:传入的参数类型、顺序及个数都必须与当前的Constructor对象一一对应,不然就会报错
public T newInstance(Object... args);  

使用实例

Class clazz = Person.class;  
Constructor constructor = clazz.getDeclaredConstructor(Integer.class, String.class);  
//是否将任何函数或字段设置为可访问的。
//如果设置为true,就不管这个函数或者字段是声明为private还是public,都是可以访问的,默认情况下是false,即只有public类型是可访问的。
//如果没有设置setAccessible(true)的话,在使用protected或者private构造函数创建实例时,会提示:(访问拒绝)
constructor.setAccessible(true);  
  
//构造实例
Person person1 = (Person) constructor.newInstance(new Integer(30),new String("harvic"));  
Log.d(TAG, "构造的参数为:" + person1.getName() + "  " + person1.getAge());  
4.1.3 得到获取到的构造函数Constructor的参数

相关函数

//用于解析一般函数  
Class[] getParameterTypes();  
//用于解析泛型对象  
Type[] getGenericParameterTypes(); 

使用实例
获取类中所有构造函数的参数类型列表

Class clazz = Person.class;  
Constructor[] constructors = clazz.getDeclaredConstructors();  
for (Constructor c:constructors){  
    c.setAccessible(true);  
    Class[] types = c.getParameterTypes();  
  
    StringBuilder builder = new StringBuilder("获取参数类型为:");  
    for(Class t:types){  
        builder.append(t.getName());  
        builder.append("   ");  
    }  
    Log.d(TAG,builder.toString());  
}  
4.1.4 得到获取到的构造函数Constructor的访问修饰符

相关函数
Constructor获取Modifier对象

int modifier = constructor.getModifiers();  

Modifier中主要有以下几个方法

//根据整型变量来生成对应的修饰符字符串  
//这个函数的作用就是根据传进来的整型,根据其中的标识位来判断具有哪个修饰符,然后将所有修饰符拼接起来输出。
String Modifier.toString(int modifiers)   
//以下这些方法来检查特定的修饰符是否存在  
boolean Modifier.isAbstract(int modifiers)  
boolean Modifier.isFinal(int modifiers)  
boolean Modifier.isInterface(int modifiers)  
boolean Modifier.isNative(int modifiers)  
boolean Modifier.isPrivate(int modifiers)  
boolean Modifier.isProtected(int modifiers)  
boolean Modifier.isPublic(int modifiers)  
boolean Modifier.isStatic(int modifiers)  
boolean Modifier.isStrict(int modifiers)  
boolean Modifier.isSynchronized(int modifiers)  
boolean Modifier.isTransient(int modifiers)  
boolean Modifier.isVolatile(int modifiers) 

使用实例

Class clazz = Person.class;  
Constructor[] constructors = clazz.getDeclaredConstructors();  
for (Constructor c:constructors) {  
    c.setAccessible(true);  
  
    int modifier = c.getModifiers();  
    Log.d(TAG,"一个访问修饰符为:"+Modifier.toString(modifier));  
} 
4.1.5 得到声明Constructor的类的Class对象

相关函数

Class getDeclaringClass();  

使用实例

Class clazz = Person.class;  
Constructor constructor = clazz.getDeclaredConstructor();  
Class declarClazz = constructor.getDeclaringClass();  
Log.d(TAG,declarClazz.getName()); 

4.2 成员变量的获取与设置 Field

4.2.1 获取Field对象

相关函数

//仅能获取声明类型为public的成员变量  
Field[] getFields();  
Field getField(String name);   //用于指定要获取的成员变量的名称 比如int age,则传入“age”
//可以获取全部的成员变量  
Field[] getDeclaredFields();  
Field getDeclaredField(String name)  //用于指定要获取的成员变量的名称 比如int age,则传入“age”

使用实例
以getDeclaredFields()为例

Class clazz = Person.class;  
Field[] fields = clazz.getDeclaredFields();  
for (Field field:fields){  
    field.setAccessible(true);  
    Class type = field.getType();  
    //field.getName():用于得到当前成员变量的名称,比如Person类中的age,name
    //field.getType():用于得到当前成员变量的类型。
    Log.d(TAG,"枚举到的field:"+type.getName()+"  "+field.getName());  
} 
4.2.2 Field的get、set操作

相关函数

//当获取或设置指定类对象中某变量的值时,可以使用Field中的get,set方法

//===成员变量的类型为派生自Object的类型===
//第一个参数object为要设置的类的对象,第二个值是此Field对象所对应的值  
void set(Object object, Object value)  
//参数是要获取值的类的对象,返回值为在此类的实例中,对应成员变量的值  
Object get(Object object) 
//直接利用set(),get()函数来设置与获取原始类型的变量,要保证所设置的Object类型与原始类型是可以相互转换的!如果不能,那将报错!最好使用专门的setInt等

//===成员变量的类型为原始的数据类型,比如int,double,char,byte等===
//设置与获取int类型的值  
void setInt(Object object, int value)  
int getInt(Object object)  
//设置与获取double类型的值  
void setDouble(Object object, double value)  
double getDouble(Object object)  
//设置与获取float类型的值  
void setFloat(Object object, float value)  
float getFloat(Object object)  
//设置与获取bool类型的值  
void setBoolean(Object object, boolean value)  
boolean getBoolean(Object object)  
//设置与获取short类型的值  
void setShort(Object object, short value)  
short getShort(Object object)  
//设置与获取long类型的值  
void setLong(Object object, long value)  
long getLong(Object object)  
//设置与获取byte类型的值  
void setByte(Object object, byte value)  
byte getByte(Object object)  
//设置与获取char类型的值  
void setChar(Object object, char value)  
char getChar(Object object)  

使用实例

Class clazz = Person.class;  
Constructor constructor = clazz.getConstructor();  
Person person = (Person)constructor.newInstance();  
  
Field fName = clazz.getDeclaredField("name");  
fName.setAccessible(true);  
fName.set(person, "qijian");  
String val = (String)fName.get(person);  
Log.d(TAG, "fieldName:" + val + "   personName:" + person.getName());  

Field fAge = clazz.getDeclaredField("age");  
fAge.setAccessible(true);  
fAge.setInt(person, 20);  
Log.d(TAG, "fieldAge:" + fAge.getInt(person) + "   personAge:" + person.getAge());  
4.2.3 Field之isEnumConstant()

相关函数

boolean isEnumConstant()  //这个函数用于判断当前field是否为枚举常量;

什么是枚举常量

//枚举类COLOR中,WHITE,BLACK,YELLOW它们三个都是枚举常量!
public static enum COLOR{WHITE,BLACK,YELLOW}  
//而private COLOR color,这个color是COLOR类型的变量!
private COLOR color; 

使用实例

Class clazz2 = Person.COLOR.class;  
Field field = clazz2.getDeclaredField("WHITE");  
Log.d(TAG,"COLOR.WHITE是否是枚举常量:"+field.isEnumConstant()+"");  
  
Class clazz = Person.class;  
Field fColor = clazz.getDeclaredField("color");  
fColor.setAccessible(true);  
boolean isEnum = fColor.isEnumConstant();  
Log.d(TAG,"color是否是枚举常量:"+isEnum); 
4.2.4 Field之Modifiers与getDeclaringClass()

相关函数

//获取该成员变量所对应的访问修饰符组所对应的Int数字  
int getModifiers()  
//获取声明该变量的类  
Class getDeclaringClass()  

使用实例

Field fAge = clazz.getDeclaredField("age");  
Class declareClazz = fAge.getDeclaringClass();  

5.3 成员函数的获取与设置 Method

5.3.1 获取Method对象

相关函数

Method[] getMethods()  
//第一个参数name:表示要获取Method对象的成员函数名
//第二个可变长参数parameterTypes:需要匹配的成员函数需要传入的参数的类型
Method getMethod(String name, Class... parameterTypes)  
  
Method[] getDeclaredMethods()  
Method getDeclaredMethod(String name, Class... parameterTypes)  

使用实例

Class clazz = Person.class;  
Method[] methods = clazz.getDeclaredMethods();  
for (Method m:methods){  
    Log.d(TAG,"枚举到的方法:"+m.toString());  
} 

// public void setName(String name)
Method method = clazz.getDeclaredMethod("setName",String.class);  
Log.d(TAG,"得到指定方法:"+method.toString());  
5.3.2 Method之Invoke

相关函数

//它的功能是用于执行Method对象所对应的函数

//Object receiver:指要执行该函数的类对象,比如我们的Person类的某一实例
//Object… args:可变长参数,用于传入该函数声明中的参数所对应的值的列表;
//返回值:Object:将函数的结果返回,Object对象就是执行此函数后返回的结果
Object invoke(Object receiver, Object... args)  

使用实例

Class clazz = Person.class;  
Person person = new Person();  
//public Boolean testInvoke(Integer age,String name)
Method method = clazz.getDeclaredMethod("testInvoke", Integer.class,String.class);  
  
method.setAccessible(true);  
Boolean result = (Boolean)method.invoke(person, 25, "I m harvic");  
Log.d(TAG,"执行结果:"+result);  

Field,Method的对象对应的不是哪个类实例中变量,而是类中变量的对象。
也就是说,Field,Method对象只有一个,是在编译时,保存在Class中的。
而Person的实例却可以有很多个,如果要改变哪个Person实例中成员变量的值,就必须将该Person实例传入Field对象的参数中,让Field对象设置。

5.3.3 Method之获取参数类型

相关函数

Class[] getParameterTypes()  
Type[] getGenericParameterTypes()  

使用实例

Class clazz  = Person.class;  
Method method = clazz.getDeclaredMethod("testInvoke", Integer.class,String.class);  
  
Class[] params = method.getParameterTypes();  
for (Class c:params){  
    Log.d(TAG,"枚举到参数类型:"+c.getName());  
} 
5.3.4 Method之获取返回值类型

相关函数

Class getReturnType()  
Type getGenericReturnType() 

使用实例

Class clazz  = Person.class;  
Method method = clazz.getDeclaredMethod("testInvoke", Integer.class,String.class);  
  
Class type = method.getReturnType();  
Log.d(TAG,"返回值类型为:"+type.getName())

你可能感兴趣的:(Android反射)