在java中,类、接口、Enum等编译后,都会生成.class文件,用来记录每一个类的具体信息。在加载.class文件时,JVM会产生一个Class实例来代表该.class文件,Class实例记录了每一个类的所有信息。通多Class实例,我们就可以实现反射机制了。
可以通过三种方法来获得java.lang.Class的实例
- 通过对象的成员函数
getClass()
来获得,该函数为java.lang.Object类的public函数,由于每一个类都是继承Object的,所以每一个类都会有该函数 - 通过
类名.class
来获得。 - 通过
Class.forName()
来获得。Class.forName
有两个版本,Class.forName("类名")
和Class.forName("类名", bool值是否加载初始化,类加载器)
对于基本类型,也可以使用对应打包类上加.TYPE来取得Class对象,例如:
使用Integer.TYPE可取得代表int基本类型的Class,如果需要取得代表Integer.class文档的Class,那么必须使用Integer.class.
拥有Class实例后,就可以通过Class实例所记录的信息来获得对应类的信息以及生成类实例。可以得到类的所有构造方法、成员函数(包括静态方法)以及成员属性(包括静态属性),还可以访问以及修改对象的私有成员属性。
1 | package chb.test.reflect; |
调用方法
在方法调用中,参数类型必须正确,这里需要注意的是不能使用包装类替换基本类型,比如不能使用Integer.class代替int.class1
2
3
4
5
6
7
8Class cls = Class.forName("chb.test.reflect.Student");
Method m = cls.getDeclaredMethod("hi",new Class[]{int.class,String.class});
m.invoke(cls.newInstance(),20,"chb");
//static方法调用时,不必得到对象
Class cls = Class.forName("chb.test.reflect.Student");
Method staticMethod = cls.getDeclaredMethod("hi",int.class,String.class);
staticMethod.invoke(cls,20,"chb");//这里不需要newInstance
private的成员变量赋值
如果直接通过反射给类的private成员变量赋值,是不允许的,这时我们可以通过setAccessible方法解决。代码示例:1
2
3
4
5
6Class cls = Class.forName("chb.test.reflect.Student");
Object student = cls.newInstance();
Field field = cls.getDeclaredField("age");
field.setAccessible(true);//设置允许访问
field.set(student, 10);
System.out.println(field.get(student));
其实,在某些场合下(类中有get,set方法),可以先反射调用set方法,再反射调用get方法达到如上效果,代码示例:1
2
3
4
5
6
7
8Class cls = Class.forName("chb.test.reflect.Student");
Object student = cls.newInstance();
Method setMethod = cls.getDeclaredMethod("setAge",Integer.class);
setMethod.invoke(student, 15);//调用set方法
Method getMethod = cls.getDeclaredMethod("getAge");
System.out.println(getMethod.invoke(student));//再调用get方法
以上的代码来自这里