相关文章:
1、《夯实JAVA基本之一 —— 泛型详解(1):基本使用》
2、《夯实JAVA基本之一——泛型详解(2):高级进阶》
3、《夯实JAVA基本之二 —— 反射(1):基本类周边信息获取》
4、《夯实JAVA基本之二 —— 反射(2):泛型相关周边信息获取》
5、《夯实JAVA基本之二 —— 反射(3):类内部信息获取》
上两篇文章中,给大家讲了,有关类周边信息的获取方法,这篇文章中我们将深入到类的内部,看看类的构造函数,内部变量,函数等信息的获取方法。相比而言,这篇更重要。
一、构造函数相关获取
1、获取构造函数
要获取类的构造函数,有下面几种方法:- //获取public类型的构造函数
- Constructor<?>[] getConstructors();
- Constructor<T> getConstructor(Class<?>... parameterTypes);
- //获取所有类型的构造函数
- Constructor<?>[] getDeclaredConstructors();
- Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
下面我们举个例子来看下getDeclaredConstructors和getDeclaredConstructor的用法。
(1)、getDeclaredConstructors()
- Constructor<?>[] getDeclaredConstructors();
首先,构造一个类Person:
- public class Person {
- private int age;
- private String name;
- public Person(){
- }
- private Person(int age, String name){
- this.age = age;
- this.name = name;
- }
- private Person(Integer age, String name){
- this.age = age;
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
- //1、枚举
- Class<?> clazz = Person.class;
- Constructor<?>[] constructors = clazz.getDeclaredConstructors();
- for (Constructor item:constructors){
- Log.d(TAG,"枚举到的构造函数:"+item.toString());
- }
- //2、根据类型,获取指定的构造的构造函数
- Constructor<?> constructor = clazz.getDeclaredConstructor(Integer.class, String.class);
- Log.d(TAG, "指定参数得到的构造函数:"+constructor.toString());
我们先看下这两个函数的意义,然后再来讲解上面的代码:
(1)、getDeclaredConstructors()
getDeclaredConstructors()将得到所有构造函数的列表,包括声明为private,protected和public的构造函数。
然后我们通过for…each循环,得到将每个构造函数打印出来。
(2)、getDeclaredConstructor(Class<?>… parameterTypes)
这个函数是通过指定构造函数的参数列表来得到指定构造函数的,有两点要非常注意:
- parameterTypes是数据类型的Class对象,如果是原始类型,那么应该对应的是int.class,double.class(原始类型也是有class对象的)
- parameterTypes一定要与要得到的构造函数中的参数声明顺序、类型及个数要完全匹配,如果多一个、少一个或类型不匹配也是找不到的。这一点是极其要注意的,getDeclaredConstructor不是模糊匹配而是精确匹配!
- Constructor<?> constructor = clazz.getDeclaredConstructor(Integer.class, String.class);
大家可能会怀疑了,我们这里好像有两个构造函数能匹配:
- private Person(int age, String name){
- this.age = age;
- this.name = name;
- }
- private Person(Integer age, String name){
- this.age = age;
- this.name = name;
- }
那clazz.getDeclaredConstructor(Integer.class, String.class);要匹配哪个呢?
由于这里是严格匹配,所以这里必然匹配的是private Person(Integer age, String name)函数,如果我们要匹配private Person(int age, String name)要怎么办呢?那获取构造函数的方法就要这么写了:
- Constructor<?> constructor = clazz.getDeclaredConstructor(int.class, String.class);
2、Constructor系列方法之构造实例
利用Constructor的对象来构造实例时,主要是使用的是newInstance方法:- public T newInstance(Object... args);
下面我们举个例子来看下这个函数的用法:
我们同样,这个例子还是基于Person类的:
- public class Person {
- private int age;
- private String name;
- public Person(){
- }
- private Person(int age, String name){
- this.age = age;
- this.name = name;
- }
- private Person(Integer age, String name){
- this.age = age;
- this.name = name;
- }
- public int getAge() {
- return age;
- }
- public void setAge(int age) {
- this.age = age;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
下面来看看,如何使用newInstance来构造Person的实例:
- Class<?> clazz = Person.class;
- Constructor<?> constructor = clazz.getDeclaredConstructor(Integer.class, String.class);
- constructor.setAccessible(true);
- //构造实例一
- Person person1 = (Person) constructor.newInstance(new Integer(30),new String("harvic"));
- Log.d(TAG, "构造的参数为:" + person1.getName() + " " + person1.getAge());
- //构造实例二
- Person person2 = (Person) constructor.newInstance(50,"qijian");
- Log.d(TAG,"构造的参数为:"+person2.getName() + " "+ person2.getAge());
- //构造实例三
- Person person3 = (Person) constructor.newInstance();
- person3.setAge(30);
- person3.setName("qijian");
- Log.d(TAG,"构造的参数为:"+person3.getName() + " "+ person3.getAge());
这段代码总共分为四部分:
第一部分:获取Constructor对象
- Class<?> clazz = Person.class;
- Constructor<?> constructor = clazz.getDeclaredConstructor(Integer.class, String.class);
- constructor.setAccessible(true);
所以匹配的是下面这个构造函数:
- private Person(Integer age, String name){
- this.age = age;
- this.name = name;
- }
- void setAccessible(boolean flag)
在反射中,总共有三种类型具有setAccessible(boolean flag)函数:Constructor,Field,Method;分别对应构造函数,成员变量和成员函数。我们知道在一个类中,也就只有构造函数,成员变量和成员函数这三部分,而且这三部分都可能会被设置为private和protected.所以它们三个都会具有设置是否可访问的setAccessible(boolean flag)函数。(后面用到还会再讲)
第二部分:使用标准的Integer,String构造实例
- //构造实例一
- Person person1 = (Person) constructor.newInstance(new Integer(30),new String("harvic"));
- Log.d(TAG, "构造的参数为:" + person1.getName() + " " + person1.getAge());
然后将constructor.newInstance的返回值强转成Person的实例,最后通过person的函数person1.getName()和person1.getAge()获取它的内容。
第三部分:使非标准,但可转换的参数来构造实例
我们再看来第二个构造方法:
- //构造实例二
- Person person2 = (Person) constructor.newInstance(50,"qijian");
- Log.d(TAG,"构造的参数为:"+person2.getName() + " "+ person2.getAge());
第四部分:如果不按规则传递参数会怎样
我们知道,我们得到的constructor对应的构造函数是:private Person(Integer age, String name),所在我们在newInstance的时候,必须声明这个Constructor时的参数类型,顺序及个数一个个传进去参数的对应值,如果我们不传、少传或者多传,结果会怎样呢?
我们来看看第三个构造实例:
- //构造实例三
- Person person3 = (Person) constructor.newInstance();
- person3.setAge(30);
- person3.setName("qijian");
- Log.d(TAG,"构造的参数为:"+person3.getName() + " "+ person3.getAge());
- public Person(){
- }
错误信息为:(参数个数不对)
3、Constructor系列方法之获取参数类型
Constructor中获取参数类型的主要有两个方法:- //用于解析一般函数
- Class<?>[] getParameterTypes();
- //用于解析泛型对象
- Type[] getGenericParameterTypes();
这里为了减轻本文理解难度,采用getParameterTypes()举例子:
下面的程序将实现,分别打印出Person.class中所有构造函数的参数类型列表:
- 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());
- }
我们看看代码是如何实现的:
首先,获取所有构造函数列表:
- 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、Constructor系列方法之获取构造函数的访问修饰符
在第一篇中,我们就已经讲过访问修饰符的概念及用法,这里不打算再细讲,仅通过一个例子来简单说明它的用法,有关详细的访问修饰符的定义及用法,请参考 《夯实JAVA基本之二 —— 反射(1):基本类周边信息获取》示例函数:打印出每个构造函数的访问修饰符
- 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));
- }
这段代码难度不大,就不再讲了。
5、Constructor系列方法之getDeclaringClass()
getDeclaringClass()方法声明如下:- Class<T> getDeclaringClass();
我们用个例子来说明下:
- Class<?> clazz = Person.class;
- Constructor<?> constructor = clazz.getDeclaredConstructor();
- Class<?> declarClazz = constructor.getDeclaringClass();
- Log.d(TAG,declarClazz.getName());
我们来说明一下这个例子
首先,得到一个默认的无参数的构造函数:
- Class<?> clazz = Person.class;
- Constructor<?> constructor = clazz.getDeclaredConstructor();
- Class<?> declarClazz = constructor.getDeclaringClass();
- Log.d(TAG,declarClazz.getName());
好了,有关Constructor所有常用函数都讲完了,下面我们看看类中其它参数的获取方法。
源码在文章底部给出
二、成员变量的获取与设置
1、获取Field对象
类中的信息,无外乎有三种,:构造函数,成员变量和成员函数。类中成员变量相关的信息都保存在Filed类型的对象里。Class中对Field对象的获取有下面四个函数:
- //仅能获取声明类型为public的成员变量
- Field[] getFields();
- Field getField(String name);
- //可以获取全部的成员变量
- Field[] getDeclaredFields();
- Field getDeclaredField(String name)
getFields()和getDeclaredFields()是得到所有能够得到的成员变量的Field数组。
getField(String name)和getDeclaredField(String name)的意义是得到指定变量名的对应的Field对象。
下面,我们就利用getDeclaredXXX()系列函数来做讲解。
(1)、getDeclaredFields()
我们依然是Person来做例子:Person.class:
- public class Person {
- private int age;
- private String name;
- public Person(){
- }
- …………
- }
我们看例子:
- Class<?> clazz = Person.class;
- Field[] fields = clazz.getDeclaredFields();
- for (Field field:fields){
- field.setAccessible(true);
- Class<?> type = field.getType();
- Log.d(TAG,"枚举到的field:"+type.getName()+" "+field.getName());
- }
我们来分析下这段代码
- Class<?> clazz = Person.class;
- Field[] fields = clazz.getDeclaredFields();
- for (Field field:fields){
- field.setAccessible(true);
- Class<?> type = field.getType();
- Log.d(TAG,"枚举到的field:"+type.getName()+" "+field.getName());
- }
这里额外用到两个函数:
- field.getName():用于得到当前成员变量的名称,比如Person类中的age,name
- field.getType():用于得到当前成员变量的类型。
(2)、getDeclaredField(String name)
此函数完整的声名如下:- Field getDeclaredField(String name)
下面我们举个例子来说明下它的用法:
- Class<?> clazz = Person.class;
- Field field = clazz.getDeclaredField("age");
- field.setAccessible(true);
- Class<?> type = field.getType();
- Log.d(TAG,"得到age对应的field:"+type.getName()+" "+field.getName());
在本例中,我们通过指定clazz.getDeclaredField(“age”)来获取变量为为age的成员变量,然后利用field.getType()获取对应Field对象的类型,利用field.getName()得到成员变量的名称。
我们这里知道了怎么得到成员变量的Field对象,下面就来看看Field中有哪些操作吧。
2、Field的get、set操作
(1)、get()与set()
当获取或设置指定类对象中某变量的值时,可以使用Field中的get,set方法,声明如下:- void set(Object object, Object value)
- Object get(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());
在上面的例子中,我们先通过Constructor得到Person的一个实例:
- 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);
在get时,传递的参数是当前要获取的Person实例,得到的是当前实例中对应成员变量的值。
我们再回过头来看看get,set函数
- //第一个参数object为要设置的类的对象,第二个值是此Field对象所对应的值
- void set(Object object, Object value)
- //参数是要获取值的类的对象,返回值为在此类的实例中,对应成员变量的值
- Object get(Object object)
(2)get系列函数与set系列函数
再回过头来看上面的get与set函数:- void set(Object object, Object value)
- Object get(Object object)
在get中,获取到的值的类型,也是Object类型的,同样,也就要求要通过这个get函数获取Field所对应的成员变量的值,此成员变量的类型就必须是派生自Object的类型!
那么问题就来了,那我们的成员变量的类型,并不一定是派生自Object的类型啊,也可能是原始的数据类型,比如int,double,char,byte等,那这些类型的成员变量的值要怎么设置与获取呢。
为了解决这个问题,Java的开发者们,额外开发了几对get与set函数:
- //设置与获取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)
下面我们就利用我们的例子来看看:
同样还是我们的Person类,因为我们的Person中的成员变量age的类型是int:
- public class Person {
- private int age;
- private String name;
- …………
- }
- Class<?> clazz = Person.class;
- Constructor<?> constructor = clazz.getConstructor();
- Person person = (Person)constructor.newInstance();
- Field fAge = clazz.getDeclaredField("age");
- fAge.setAccessible(true);
- fAge.setInt(person, 20);
- Log.d(TAG, "fieldAge:" + fAge.getInt(person) + " personAge:" + person.getAge());
在这个例子中,我们先通过setInt将20设置进person实例的age变量中,然后分别通过fage.getInt与person自己的person.getAge()取出。
没什么难度,就不再细讲了。
(3)、给原始类型使用set,get函数
我们再尝试一下,如果我给声明为原始类型的变量使用set,get函数,能通过吗?看看下面的代码:
- Class<?> clazz = Person.class;
- Constructor<?> constructor = clazz.getConstructor();
- Person person = (Person)constructor.newInstance();
- Field fAge = clazz.getDeclaredField("age");
- fAge.setAccessible(true);
- fAge.set(person, 30);
- Integer ageval = (Integer)fAge.get(person);
- Log.d(TAG, "fieldAge:" + ageval.toString() + " personAge:" + person.getAge());
首先,这段代码是可以通过的,而且也有输出的值,我们再来看看,我们都做了什么。
最关键的是这段:
- fAge.set(person, 30);
- Integer ageval = (Integer)fAge.get(person);
首先,这里的数字30,编译器会强转成new Integer(30),做为参数传给set函数中。
而在真正给age设置时,Integer类型也是可以强转成Int类型的!所以不会出问题。
在通过get取出时,也是一样的,正是由于int和Integer是可以相互转换的,所以直接利用get与set是不会出问题的。
所以结论出来了:如果我们直接利用set(),get()函数来设置与获取原始类型的变量,要保证所设置的Object类型与原始类型是可以相互转换的!如果不能,那将报错!
所以,为了保险起见,如果遇到原始类型的变量,我们还是使用专门针对原始类型而设置的setxxx,getxxx系列函数为好。
3、Field之isEnumConstant()
该函数的声明为:- boolean isEnumConstant()
我们先说明一下,什么是枚举常量,注意是常量!不是变量!
下面这个例子中,定义一个枚举COLOR,然后利用COLOR定义一个color变量:
- public static enum COLOR{WHITE,BLACK,YELLOW}
- private COLOR color;
而private COLOR color,这个color是COLOR类型的变量!
下面我们同样在Person的基础上举个例子:
我们先给Person添加一个枚举:
- public class Person {
- public static enum COLOR{WHITE,BLACK,YELLOW}
- private int age;
- private String name;
- 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);
我们从结果中可以看到,首先COLOR枚举类中的WHITE是一个枚举常量,而利用COLOR定义的color变量不是枚举常量
我们再来看看代码,先看看COLOR部分:
- Class<?> clazz2 = Person.COLOR.class;
- Field field = clazz2.getDeclaredField("WHITE");
- Log.d(TAG,"COLOR.WHITE是否是枚举常量:"+field.isEnumConstant()+"");
然后,我们再看看操作color的代码:
- Class<?> clazz = Person.class;
- Field fColor = clazz.getDeclaredField("color");
- fColor.setAccessible(true);
- boolean isEnum = fColor.isEnumConstant();
- Log.d(TAG,"color是否是枚举常量:"+isEnum);
4、Field之Modifiers与getDeclaringClass()
- //获取该成员变量所对应的访问修饰符组所对应的Int数字
- int getModifiers()
- //获取声明该变量的类
- Class<?> getDeclaringClass()
getDeclaringClass():获取声明该成员变量的类的Class对象。比如这里声明int age的类是Person,所以:
- Field fAge = clazz.getDeclaredField("age");
- Class<?> declareClazz = fAge.getDeclaringClass();
这个declareClazz的值就是Person.Class,这个函数的用法,我们在讲解Constructor的时候已经讲过了,不再讲了。
源码在文章底部给出
三、成员函数的获取与设置
1、获取Method对象
与成员变量对应的类型为Field类似,类中的方法对应的类型是Method.获取指定类中的成员函数的方法有下面四种方法:
- Method[] getMethods()
- Method getMethod(String name, Class<?>... parameterTypes)
- Method[] getDeclaredMethods()
- Method getDeclaredMethod(String name, Class<?>... parameterTypes)
下面我们针对getDeclaredMethods()和getDeclaredMethod()来举个例子说明其用法:
(1)、getDeclaredMethods()
该函数能够获取类中所有声明的函数(不包括构造函数),举个例子来看他使用方法:- Class<?> clazz = Person.class;
- Method[] methods = clazz.getDeclaredMethods();
- for (Method m:methods){
- Log.d(TAG,"枚举到的方法:"+m.toString());
- }
上面的例子很容易理解,首先通过 clazz.getDeclaredMethods()得到Person类中,所有的成员函数,然后利用for…each循环将它们逐个打印出来
(2)、getDeclaredMethod(String name, Class<?>… parameterTypes)
该函数的完整声明如下:- Method getDeclaredMethod(String name, Class<?>... parameterTypes)
第二个可变长参数parameterTypes:需要匹配的成员函数需要传入的参数的类型,这里需要非常注意,这里的getDeclaredMethod是精确匹配的,如果我们传入的函数名以及参数类型、顺序、个数与该函数声明不一致的话,就会报错
值得非常注意的是,当我们要找的方法所对应的参数类型是原始数据类型,比如Int,double等,它们所对应的Class对象为int.class,double.class即可;所以原始类型也是有Class对象的!
下面我们举个例子来看:
我们还是用Person类,我们知道Person类中有一个setName(String name)函数:
- public class Person {
- public static enum COLOR{WHITE,BLACK,YELLOW}
- private int age;
- …………
- public void setName(String name) {
- this.name = name;
- }
- }
- Method method = clazz.getDeclaredMethod("setName",String.class);
- Log.d(TAG,"得到指定方法:"+method.toString());
从代码中也可以看出getDeclaredMethod很容易使用,与我们讲解的用法一样,第一个参数传入函数名,第二个参数逐个传入该函数的参数类型。然后就可以得到该函数的Method对象了。
2、Method之Invoke
Invoke函数无疑是Method类中最重要的方法,它的功能是用于执行Method对象所对应的函数。完整的声明如下:- Object invoke(Object receiver, Object... args)
Object receiver:指要执行该函数的类对象,比如我们的Person类的某一实例
Object… args:可变长参数,用于传入该函数声明中的参数所对应的值的列表;
返回值:Object:将函数的结果返回,Object对象就是执行此函数后返回的结果
下面我们看个例子来说明下它的用法:
我们在Person中单独再加一个函数:
- public class Person {
- …………
- public Boolean testInvoke(Integer age,String name){
- Log.d("qijian","得到参数age:"+age +" name:"+name);
- return true;
- }
- }
下面我们来看看如何使用Method.Invoke函数来执行testInvoke()
- Class<?> clazz = Person.class;
- Person person = new Person();
- 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);
结果如下:
第一部分,得到Person实例与testInvoke所对应的Method对象:
- Class<?> clazz = Person.class;
- Person person = new Person();
- Method method = clazz.getDeclaredMethod("testInvoke", Integer.class,String.class);
- method.setAccessible(true);
- Boolean result = (Boolean)method.invoke(person, 25, "I m harvic");
最后invoke会将函数执行结果以Object类型返回。我们可以将它强转为我们需要的类型。
这里总结一下,从Field,Method中都可以看出,Field,Method的对象对应的不是哪个类实例中变量,而是类中变量的对象。
也就是说,Field,Method对象只有一个,是在编译时,保存在Class中的。而Person的实例却可以有很多个,如果要改变哪个Person实例中成员变量的值,就必须将该Person实例传入Field对象的参数中,让Field对象设置。
3、Method之获取参数类型
与其它类的获取参数类型一样,这里同样针对普通变量和泛型有两种获取参数类型的方法:- Class<?>[] getParameterTypes()
- Type[] getGenericParameterTypes()
getGenericParameterTypes用于获取带有泛型参数的类型,有关泛型的获取比较复杂,我们单独用一篇来讲述有关泛型相关方法的获取,如要必须请大家参考 《夯实JAVA基本之二 —— 反射(2):泛型相关周边信息获取》
这里我以getParameterType来举例子:(同样使用Person类中的testInvoke函数)
- 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());
- }
这段代码很好理解,首先利用clazz.getDeclaredMethod找到testInvoke所对应的Method对象。
然后利用method.getParameterTypes()得到testInvoke的参数列表。最后参数枚举打印出来
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());
这段代码也非常简单,首先得到Person类中成员函数testInvoke的Method对象;然后利用method.getReturnType()得到它的返回值类型。
好了,到这里反射相关的所有内容就讲完,一句话,涉及面太TM广了……有关泛型相关的部分是比较难的,大家可能需要多看几篇,下面我们举两个例子来看在实际代码中,反射的用法
源码在文章底部给出
四、实战实例讲解
1、实例一:Fragment.instantiate(Context context, String fname, @Nullable Bundle args)
在V4包中,Fragment有一个函数,可以根据指定Fragment的路径名来得到对应Fragment的实例。它的完整声明如下:
- public static Fragment instantiate(Context context, String fname, Bundle args)
- context:Context类型对象;
- fname:对应Fragment类的完整的路径名,可以利用Class.getName()得到。
- args:是在构造Fragment实例时需要的Bundle参数。
(1)、使用方法
我们先看下Fragment.instantiate的用法:我们先引入v4包,然后新建一个Fragment:
- public class DemoFragment extends Fragment {
- public void printFragment(String name){
- Log.d("qijian",name+"就知道你是个逗逼");
- }
- }
然后我们来看看怎么利用Fragment.instantiate来获取它的实例:
- Class<?> clazz = DemoFragment.class;
- DemoFragment fragment = (DemoFragment) Fragment.instantiate(this,clazz.getName(),null);
- fragment.printFragment("harvic");
然后利用Fragment.instantiate(this,clazz.getName(),null);获取它的实例。
这里需要注意的是,我们传进去的完整的路径名,通过clazz.getName()得到。
然后利用fragment.printFragment(“harvic”);执行DemoFragment中的printFragment函数。
执行结果如下:
(2)、原理
上面学会了使用Fragment的instantiate函数,下面就来看看这个函数内部是如何实现的吧:- public static Fragment instantiate(Context context, String fname, Bundle args) {
- try {
- Class e = (Class)sClassMap.get(fname);
- if(e == null) {
- e = context.getClassLoader().loadClass(fname);
- sClassMap.put(fname, e);
- }
- Fragment f = (Fragment)e.newInstance();
- if(args != null) {
- args.setClassLoader(f.getClass().getClassLoader());
- f.mArguments = args;
- }
- return f;
- } catch (ClassNotFoundException var5) {
- …………
- }
- }
第一部分,根据fName得到对应Fragment的Class对象:
- Class e = (Class)sClassMap.get(fname);
- if(e == null) {
- e = context.getClassLoader().loadClass(fname);
- sClassMap.put(fname, e);
- }
第二部分:构造实例与设置参数:
这里首先,通过Class.newInstance()函数得到对应Fragment的实例,然后将我们传入的Bundle参数args加入到Fragment的mArguments参数对象中。
最后将Fragment的实例f返回。
2、实例二:PopupWindow之setTouchModal(boolean touchModal)
如果我想要一个效果,点击PopupWindow外部区域,弹窗不消失,但是点击事件会向下面的activity传递,这时候就需要用到PopupWindow的setTouchModal(boolean touchModal)函数。但setTouchModal(boolean touchModal)函数完整声明为:
- /**
- * Set whether this window is touch modal or if outside touches will be sent to
- * other windows behind it.
- * @hide
- */
- public void setTouchModal(boolean touchModal) {
- mNotTouchModal = !touchModal;
- }
下面我们来举个例子来调用这个函数:
- public static void setPopupWindowTouchModal throws Exception(PopupWindow popupWindow,
- boolean touchModal) {
- if (null == popupWindow) {
- return;
- }
- Method method = PopupWindow.class.getDeclaredMethod("setTouchModal",boolean.class);
- method.setAccessible(true);
- method.invoke(popupWindow, touchModal);
- }
到这里,有关反射所有内容就讲完了,这几篇文章无论是难度还是篇幅都是比较难的,大家能坚持看完也真是太不容易了。
本文所涉及函数如下:
Constructor相关
- /**
- * 获取构造函数
- */
- //获取public类型的构造函数
- Constructor<?>[] getConstructors();
- Constructor<T> getConstructor(Class<?>... parameterTypes);
- //获取所有类型的构造函数
- Constructor<?>[] getDeclaredConstructors();
- Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
- /*
- * 获取对应类的实例
- */
- public T newInstance(Object... args);
- /**
- * 获取参数类型
- */
- //用于解析一般函数
- Class<?>[] getParameterTypes();
- //用于解析泛型对象
- Type[] getGenericParameterTypes();
- /**
- * 获取访问修饰符
- */
- int getModifiers()
- /**
- * 获取声明Constructor的类的Class对象;
- */
- Class<T> getDeclaringClass()
Field相关
- /**
- * 获取Field对象
- */
- //仅能获取声明类型为public的成员变量
- Field[] getFields();
- Field getField(String name);
- //可以获取全部的成员变量
- Field[] getDeclaredFields();
- Field getDeclaredField(String name)
- /**
- * get,set方法系列
- */
- //当获取或设置指定类对象中某变量的值
- void set(Object object, Object value)
- Object get(Object object)
- //设置与获取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)
- /**
- * 这个函数用于判断当前field是否为枚举常量;
- */
- boolean isEnumConstant()
Method相关
- /*
- * 获取指定类中的成员函数Method对象
- */
- //获取声明为public的成员函数
- Method[] getMethods()
- Method getMethod(String name, Class<?>... parameterTypes)
- //获取所有的成员函数
- Method[] getDeclaredMethods()
- Method getDeclaredMethod(String name, Class<?>... parameterTypes)
- /*
- * 执行某个方法
- */
- Object invoke(Object receiver, Object... args)
- /**
- * 获取参数类型
- */
- Class<?>[] getParameterTypes()
- Type[] getGenericParameterTypes()
- /**
- * 获取返回值类型
- */
- Class<?> getReturnType()
- Type getGenericReturnType()
本文涉及源码下载地址:http://download.csdn.net/detail/harvic880925/9314371
请大家尊重原创者版权,转载请标明出处:http://blog.csdn.net/harvic880925/article/details/50107951 谢谢
参考文章:
1、《 一个例子让你了解Java反射机制》
2、《Java反射中Method类invoke方法的用法》
3、《【译】2. Java反射——Class对象》(系列译文非常棒)
4、《反射得到参数化类型中的类型参数》
5、《提炼Java Reflection》
6、《实例讲解:java reflection 反射机制》
7、《Class.forname() 和 ClassLoader.loadClass()的区别 (转)》
8、《接口 ParameterizedType》
9、《ParameterizedType.getOwnerType() 函数怎么用?》
10、《详解Java泛型type体系整理》
11、《Java类型中ParameterizedType,GenericArrayType,TypeVariabl,WildcardType详解》
12、《谈谈Java类型中ParameterizedType,GenericArrayType,TypeVariabl》
13、《泛型与反射的使用总结之反射篇》
14、《Field,Method,Constructor— java 7 Reflection(三)》
15、《 Java 反射机制》
16、《试用Java中的反射reflect之getDeclaredMethods和getMethods》
17、《Java Reflection 相关及示例》
18、《 Android PopupWindow的使用和分析》
19、《彻底搞懂Java ClassLoader》
20、《JVM学习笔记(二)------Java代码编译和执行的整个过程》
21、《Java程序编译和运行的过程》
22、《Java类加载/ClassLoader》