最近温习了一下java的反射机制,发现之前学习反射的时候没有留下只言片语,补上补上!!!
反射的概念:
比如我们想看看我们自己长什么样子,我们自己肯定看不到自己长什么样子,所以,我们借助镜子,通过镜子的反射看到我们的样子,可以看清我们自己的五官;
同理,在Java中运行的类,也有这么一面镜子,可以反射该类的一些行为和属性,而这个反射就体现在java.lang.Class中;
通过Class对象,可以得到某个类的一些行为和属性,甚至我们通过反射可以操作这个对象的行为和属性;这就是反射机制;
要想理解反射,我们先来了解一下java.lang.Class这个‘万能’的类;
获取Class实例的几种方式:
(1)通过全类名获取Class实例,这也是最常用的一种方式
public void testClassForName(){
String className = "com.test.bean.Preson";
Class clazz = Class.forName(className);
}
为什么说这是一种最常用的一种方式,相信大家都用过Spring或者MyBatis等等这类框架,在使用这类框架的时候,免不了与该框架的XML配置文件打交道,在很多配置的地方都会填写一个全类名;
看过源码的同学应该就知道,因为这些框架会先解析XML配置文件的到这个全类名,通过这个全类名来得到Class对象,完成后面的反射调用的动作;
(2)通过getClass的方式得到Class实例
public void testGetClass(Object obj){
Class clazz = obj.getClass();
}
这里为什么用Object作为入参呢?因为更多时候我们不知道外面传进来的是什么对象,使用Object接受就可以接受任何java对象,那么通过反射就可以获得到相应的Class
(3)通过类.class的形式获取
public void testClass(){
Class clazz = Person.class;
}
(4)通过类加载器的形式获取
public void testClassLoader(){
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz = classLoader.loadClass(className);
}
也就是说,我们得到Class对象,就可以为所欲为了,可以利用反射机制来调用该类的方法或者属性;
说完Class,我们说下几个常用的相关类;这几个类的使用方式都差不多;
java.lang.reflect.Constructor
这个类是专门描述某个类的构造方法,可以通过Class对象获得到类中定义的构造方法;
//获取构造器
public void testConstructor() throws Exception{
String className = "com.test.bean.Person";
Class clazz = Class.forName(className);
//通过Class对象获取Constructor对象
Constructor constructor = clazz.getConstructor();
System.out.println(constructor.getName());
}
parameterTypes:表示构造器的参数列表,如果想获取无参构造器,这个参数不要传了;
但是这个clazz.getConstructor();只能获取到构造器为public类型的,不能获取到private修饰的构造器;
//获取私有构造器
public void testConstructor() throws Exception{
String className = "com.test.bean.Person";
Class clazz = Class.forName(className);
//通过Class对象获取private 修饰的Constructor对象
Constructor constructor = clazz.getDeclaredConstructor();
System.out.println(constructor.getName());
}
同时,如果想调用私有的构造器,调用前要调用一下setAccessible(true),获取调用私有方法的权限,否则会报异常
//获取私有构造器,并执行私有构造器
public void testConstructor() throws Exception{
String className = "com.test.bean.Person";
Class clazz = Class.forName(className);
//通过Class对象获取private 修饰的Constructor对象
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
//调用构造器创建实例
Object obj = constructor.newInstance();
System.out.println(constructor.getName());
}
通过上面,就会发现,Java的三大特性:封装、继承、多态,这里面的封装对反射来说是无用了,通过反射可以破坏他的封装;啊哈哈哈。。。
java.lang.reflect.Method
这个类描述了类中的方法,通过这个Method对象,可以操作类中定义的方法;
//通过反射获得类定义的Mehtod,但是获取不到private修饰的方法
Mehtod method = clazz.getMethod('setName', String.class);
//获取本类中定义的方法,包括私有方法也可以获取
Method method = clazz.getDeclaredMethod("test",String.class);
获取指定的Method的时候需要传入两个参数:
1.方法名
2.方法的参数列表类型,比如方法的入参是String类型的,那么这里就是String.class
//获取本类和父类中public的所有方法集合,但是获取不到私有方法
Method[] methods = clazz.getMethods();
//获取本类所有方法集合,包括私有方法
Method[] methods = clazz.getDeclaredMethods();
同样,在调用private修饰的方法之前,要调用setAccessible(true);
//通过method调用方法,这个才是重点
public Object invoke(Object obj,Object... args);
obj:调用哪个对象中的方法
args:方法的参数
method.invoke(clazz.newInstance(), "helloWorld");
java.lang.reflect.Field:属性
public void testField() throws Exception{
String className = "com.test.classloader.bean.Person";
Class clazz = Class.forName(className);
//先通过反射获得到实例
Object obj = clazz.newInstance();
//获取对象中指定的属性
Field field = clazz.getField("name");
//为属性赋值
field.set(obj, "hello");
System.out.println(obj);
//获取private 修饰的成员属性
Field field1 = clazz.getDeclaredField("name");
field1.setAccessible(true);
field1.set(obj, "world");
System.out.println(obj);
}