---------------------- android培训、java培训、期待与您交流! ----------------------
反射的基础Class类
反射并不是java1.5中的特性,而是java1.2版本中就具备这个特性。先来了解反射所需的基础Class类。
Java程序中的各个Java类(.class文件)属于同一类事物,描述这类事物的Java类名就是Class类。Class类十分特殊,它和一般classes一样继承自Object类,它没有公有的构造方法,不能实例化对象,当一个类被加载,或当加载器(classloader)的defineClass()被JVM调用时,JVM 便自动产生一个Class类对象。
获取字节码文件对应的实例对象的三种方法(Class类型)
1.类名.class。例如:System.class;
2.对象.getClass,例如:new Person().getClass;
3.Class.forName(“类名”),例如:Class.forName(“java.until.Date”); forName()这个方法是Class类中的一个静态方法,它返回与带有给定字符串名的类或接口相关联的Class
对象。反射机制中主要运用到的就是该方法,因为在编写源程序的时候还不知道类名。
说明:一个类源文件编译之后生成的字节码文件,通过以上三种方式获取到的实例对象是同一个。
九种预定义的Class实例对象,包括基本的Java的八种基本数据类型(boolean
、byte
、char
、short
、int
、long
、float
和double
)和关键字void
的Class
对象。当获取一个未知的Class实例对象之后,可以通过isPrimitive()方法来判断该对象是否属于基本数据类型的Class实例对象
总之,只要是在程序中出现的类类型,都有各自的Class实例对象,例如:int[],void,…,他们都有各自的Class实例对象。
反射的概念
反射就是把java中的各种成分映射成相应的java类。例如,一个java类中用一个Class类的对象来表示,一个类中的组成部分有构造方法,成员变量,成员方法,包等。这些信息也用一个java类来表示。表示java类的Class类显然要提供一系列的方法,来获取其中的构造方法,成员变量,成员方法,包等信息。这些信息就是用相应类的实例对象来表示,它们所对应的类分别是Constructor类,Filed类,Method类,Package类等。
构造方法的反射-Constructor类
Constructor类是java.lang.reflect包中的一个类,它的实例对象代表某个类中的一个构造方法。但是这个类本身不提供构造方法,那怎么获取该类的对象呢?在Class类中,提供了用于获取Constructor类对象的方法。
1.ConstructorgetConstructor(Class<?>... parameterTypes
),它返回一个Constructor
对象,它表示此Clas
s
对象所表示的类的指定公共构造方法。
那么,要如何获取目标类中的指定的构造方法呢?可以通过向该方法传递参数的形式来获取这个指定的构造方法,注意:所传入的参数也必须是一个java的Class类对象。比如:
Constructor con = String.class.getConstructor(StringBuffer.class);
2.Constructor[]getConstructors(),获取目标类中的所有公共构造方法,返回一个Constructor类对象数组。
所以,在获取Constructor
类
对象之前,首先要获取目标类的Class实例对象。
获取到某个java类中的构造方法的Constructor类实例对象之后,就可以调用Constructor类中的方法来实例化对象。
1.T newInstance(Object…),使用此Constructor
对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。比如:
String str = (String)con.newInstance(newStringBuffer(“abc”));
实际上,Class类中也有一个newInstance()方法,它的内部就是调用Constructor的无参newInstance()方法,所以要想通过这种方式来创建对象一定要保证类有一个无参构造方法。
成员变量的反射-Filed类
Field类也是java.lang.reflect包中的一个类,它的实例对象代表类中的某一个成员变量。同样,Field类不提供构造方法,要获取Field类的实例对象,同样首先要获取目标类的Class实例对象。
1.FieldgetField(String name),返回一个Field
对象,它反映此Class
对象所表示的类或接口的指定公共成员字段。根据所接收的参数来指向对应的目标类中的成员变量。比如:
Field field = 目标类对象.getClass().getField(“成员变量名”);
2.Field[] getFields(),返回一个包含某些Field
对象的数组,这些对象反映此Class
对象所表示的类或接口的所有可访问公共字段。
3.Field getDeclearedField(String name),返回一个Field
对象,该对象反映此Class
对象所表示的类或接口的指定已声明字段。
注意:getField()和getDeclearedField()的区别在于前面这个方法值反映目标类中公共的成员变量,而后面这个方法反映的是目标类中已申明的成员变量。
那么Field类中主要提供了哪些方法呢?
1.Object get(Objectobj),该方法返回指定对象上此Field
表示的字段的值。
field.get(“目标类对象”);
如何指向目标类中被private所修饰的成员变量呢?
步骤一:要用Constructor类中的getDeclearedField()方法反映目标类中被private修饰的成员变量。
Field field = 目标类对象.getClass().getDeclearedField(“成员变量名”);
步骤二:然后调用Field父类AccessableObject类中的void setAccessable(boolean flag)方法设置目标类中的成员变量为可访问。
field. setAccessable(true);
步骤三:调用get()方法获取相应成员变量的值。
field.get(“目标类对象”);
成员方法的反射-Method类
Method类也是java.lang.reflect包中的一个类,它的实例对象代表类中的某一个成员方法。同理Field类不提供构造方法,要获取Method类的实例对象,同样首先要获取目标类的Class实例对象。
1.MethodgetMethod(String name, Class<?>… parameterTypes),该方法返回一个Method实例对象,它反映此Class对象所表示的类或接口的指定公共成员方法。该成员方法是根据getMethod方法所接收到的参数来确定的。比如:
//获取Method类实例对象,此对象反映了String类中参数为int类型的charAt()方法.
Method reflectMethod= String.class.getMethod(“charAt”,int.class);
2.Object invoke(Objectobj, Object…args),Method类中的方法,该方法激发Method类实例对象所反映的目标类中的成员方法,参数根据目标类中的成员方法传入,返回值类型也和其相同。
char ch = reflectMethod.invoke(“目标类实例对象”, 1);
注意:如果invoke()方法接收到的第一个参数是null,则表示Method实例对象对应的是一个静态方法,因为静态方法不需要对象调用,所以接收的参数就是null。
对接受数组参数的成员方法进行反射
当目标类中的成员方法所接受的参数是一个数组时,调用invoke()方法激发该方法时,invoke()方法所接收的参数不能是一个数组类型。原因是按照java1.5的语法,整个参数是一个数组,而按照java1.4的语法,数组中的每个元素对应的是一个参数,由于java1.5要兼容java1.4的语法,所以虚拟机会按照java1.4的语法进行处理,即把数组拆开成为若干个单独的参数。所以,在反射参数为数组的成员方法时,不能使用代码:
reflectMethod.invoke(“目标类对象名”, newint[]{“…”});
因为javac会把它当做1.4的语法进行处理,所以会出现参数个数不匹配的异常。那么要如何解决这样的问题呢?
此时,可以将接收的数组参数进行装饰,用new Object[]{new int[]{“…”}}方式将参数修饰,或者将数组强制转换成为Object类型。如下:
reflectMethod.invoke(“目标类对象”, newObject[]{new int[]{“…”}}); 或者
reflectMethod.invoke(“目标类对象”, (Object)newint[]{“…”});
此时,编译器编译时就不会把它当做数组看待,也就不会把数组再拆开成若干个参数了。
---------------------- android培训、java培训、期待与您交流! ----------------------