目录
什么是反射:当我们得到一个Object时,我们不知道这个对象具体是哪一个类,但我们又需要操作这个对象里的方法或者变量时,我们可以通过反射得到这个对象的类信息。
先介绍一下Java里一个使用一个类之前JVM里发生了什么。
当程序使用某个类时,JVM会连续完成类的加载,连接和初始化这三个步骤。后面两个步骤先不考虑,就单独说说加载。类加载就是将类的class文件读入内存,并且为之创建一个java.lang.Class对象。这个java.lang.Class对象里记录着某一个类的类信息。反射获取的Class对象就是这个java.lang.Class对象。
下面开始总结通过反射得到类信息和操作类中的成员。
1、通过反射获取Class对象
Java程序里获取Class对象有三种方式
使用Class类的forName(String className)的静态方法。
调用某个类的class属性来获取Class对象。比如Person.class就会返回Person类的Class对象。这个方法比第一个方法要好一些,因为这是调用属性,不是调用方法,性能更好。而且在编译阶段程序就可以检查需要访问的Class对象是否存在。
调用某个对象的getClass()方法。
得到Class对象之后,就可以通过这个Class对象来获取其它信息了
2、通过反射获取类Class中的构造方法类Constructor
第一种方法
Constructor<T> getConstructor(Class<?> parameterTypes)
这是返回此Class对象对应类的带指定形参列表的public构造器。其中的参数都是Class。比如说,某一个构造器是这样写的,参数类型是String,
A(String s){
s = "hello";
}
那么在利用反射获取构造器时参数是String.class。比如下面这样的写法。
Class<A> a = A.class;
Constructor<T> constr = a.getConstructor(String.class);
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
第二种方法
Constructor<?>[] getContructors()
返回此Class对象对应类的所有public构造器。
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
第三种方法
Contructor<T> getDeclaredConstructor(Class<?> ...parameterTypes)
返回此Class对象对应类的带指定形参列表的构造器,并且和构造器的访问权限无关。
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
第四种方法
Constructor<?>[] getDeclaredConstructors()
返回此Class对象对应类的所有构造器,并且与构造器的访问权限无关。
3、通过反射获取类Class中的方法类Method
第一种方法
Method getMethod(String name, Class<?>...parameter)
获取指定名称,指定参数的public方法,
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
第二种方法
Method[] getMethods()
获取所有的public方法。
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
第三种方法
Method getDeclaredMethod(String name, Class<?>...parameter)
获取指定名称指定参数的方法,与访问权限无关。
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
第四种方法
Method[] getDeclaredMethods()
获取所有的方法,与访问权限无关。
4、通过反射获取类CLASS中的成员变量类Field
第一种方法
Field getField(String name)
返回类中对应名称的,public的成员变量。
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
第二种方法
Field[] getFields()
返回类中所有的public成员变量。
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
第三种方法
Field getDeclaredField(String name)
放回类中对应名称的成员变量,与访问权限无关。
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
第4中方法
Field[] getDeclaredFields()
返回所有成员变量,与访问权限无关。
5、访问CLASS对应类包含的Annotation
6、访问CLASS对应类包含的内部类
下面是通过反射生成并操作对象,在JavaEE框架中就常常会用到
7、通过反射获取对象实例
Java程序里获取Class对象有两种方式
使用Class类中的newInstance()方法:这个方法其实是调用默认构造器来生成实例,但是不推荐,java文档中已经弃用了。比较推荐下面的用法。
通过Class类获取Constructor对象,再调用Constructor中的newInstance()方法来得到类的实例。
得到对象实例之后,就可以通过这个实例来获取其它信息了
8、通过反射使用对象实例的方法
在上面提到了如果获取某一个类方法的Method对象,下面假设已经获得的Method对象名是method。
method里只是储存方法的信息,如果要调用方法则必须使用Method中的invoke方法,如下所示
Object inovke(Object obj, Object…parameter)
我们要通过具体的实例obj调用方法。invoke的第二个参数是调用方法所需要的参数,类型都是Class。
invoke返回的是调用方法的返回值。具体实例如下
class A{
int d;
public A(int a, int b, int c){
d = 2;
}
public int a1(){
System.out.println("a1");
return 1;
}
}
public class test{
public static void main(String[] args)throws Exception{
Class clazz = A.class;
Class[] buff = new Class[3];
buff[0] = int.class;
buff[1] = int.class;
buff[2] = int.class;
A a = new A(1,2,3);
Method method = clazz.getMethod("a1");
System.out.println(method.invoke(a));
}
}
输出如下
通过反射操作方法必须是操作某一具体实例的方法,即使这个方法是static方法——属于类方法,那也必须通过某一具体实例。
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
这里说一下使用反射操作方法时涉及到的权限问题。可以将反射看成一般的函数调用,符合一般的函数调用的权限规则。比如将上面代码中类A的方法a1的作用域改成private,反射调用方法就会出现错误。如果还是想调用private方法,那就要使用Method中的setAccessible方法
void setAccessible(boolean flag)
如果flag设为true,表明这个方法可以无视一般的权限规则,随意调用;如果flag设为false,表明这个方法要遵守一般的权限规则。默认为false。
class A{
int d;
public A(int a, int b, int c){
d = 2;
}
private int a1(){
System.out.println("a1");
return 1;
}
}
public class test{
public static void main(String[] args)throws Exception{
Class clazz = A.class;
Class[] buff = new Class[3];
buff[0] = int.class;
buff[1] = int.class;
buff[2] = int.class;
A a = new A(1,2,3);
Method method = clazz.getDeclaredMethod("a1");
method.setAccessible(true);
System.out.println(method.invoke(a));
}
}
9、通过反射访问类中的成员变量
在上面提到了获取Field类的方法,假设已经获取了某一类的Field类实例变量名为field。
Field类主要提供了获取成员变量的值和修改成员变量的值两个方法。先说前者。
xxx getXxx(Object obj) //这是获取某一具体类实例的成员变量值。obj是具体的类实例,
//xxx是成员变量的类型。比如下面这些代码成员变量分别是char,int和boolean。
char getChar(Object obj)
int getInt(Object obj)
boolean getBoolean(Object obj)
//如果成员变量类型是引用,那么就应该省略xxx。比如某一成员变量的类型是类A,那么获取该成员变量的方法是
Object get(Object obj)
改变成员变量的值也差不多
xxx getXxx(Object obj, xxx val) //这是改变某一具体类实例的成员变量值。obj是具体的类实例,val是要修改的值。