1.什么是反射
引用百度词条的解释为:java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。
相信看了这个有些同学或许还是有点迷糊,回答不上来什么是反射。那我们现在就具体的来实现一个简单的反射。
2.反射的小案例
假如我这里有一个cat类,cat类当中有属性,有方法,按照一般的思路步骤我们想要获取cat类当中的属性和调用当中的方法,第一步应该是先new一个对象,然后通过new的对象去调用方法。这个是理所当然的,最先想到的,也是完全正确的。那如果现在想要使用不那么常规的方法比如反射来创建对象并调用方法呢,来我们具体看代码。
package reflect01;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws Exception{
Class aClass = Class.forName("reflect01.Cat"); //这个是cat类存放的位置,包名加类名
Object o = aClass.newInstance(); //创建由此类对象表示的类的新实例(就是cat类)
Method[] methods= aClass.getDeclaredMethods(); //将cat类当中的所有方法存放到methods
//数组当中
for (int i = 0; i <methods.length ; i++) {
methods[i].invoke(o); //循环调用所有的方法
}
}
}
class Cat {
public String name;
public Integer age;
public void say(){
System.out.println("这是一只可爱的小花猫");
}
public void run(){
System.out.println("小猫猫跑的快");
}
}
上面的代码是不是突然就不认识,有点难以琢磨了?其实这个代码完成的功能依然是创建cat类,然后调用方法和属性(这里调用属性我省略了,代码过多了,不易于阅读和理解,最主要是分析起来也多了~~)。
Class aClass = Class.forName("reflect01.Cat");
解释这句代码时先解释一下Class类,在jdk1.8的官方文档是这么解释Class类的:“Class类的类
表示正在运行的Java应用程序中的类和接口。”而在一开始给的反射的定义是这么说的,“在程序的运行状态中,可以构造任意一个类的对象”。这样一对比是不是就更清楚的知道这个Class类了,这里需要特别的强调一下,一定要记住Class它是一个类,是java.lang下的一个类。
好,我们来对上面这个代码做一个分析吧,我们首先使用了Class类当中的一个静态方法forName(),进行了一个类加载得到了一个Class类型的返回值,有些同学可能已经注意到了这个静态方法传入的是一个字符串(包名+类名或者类的完全限定名称),其实这里才是整个反射机制的妙用,好奇心很强的同学可以先自行想想哦。继续,通过这个静态方法我们就在内存当中手动加载了cat类。注意,如果一开始你已经new过了,那么此时这个方法就不会被加载,因为一个类在内存当中只会被加载一次(这个不知道的话,就得再好好看看书咯)。
来,我们再继续研究一下这个方法,jdk文档中已经明确说明当我们使用这个方法时,他具体执行如下:
能够看到这就是个类加载器,现在我们应该能比较清楚地知道这个静态方法真正的作用了。
Object o = aClass.newInstance();
继续,jdk文档当中的解释为:创建由此类
对象表示的类的新实例,返回由该对象表示的新分配的类的实例。通俗的讲就是将我们为刚刚在内存当中加载的类分配一个具体的实例,等同于我们new一个对象,然后把该对象赋给一个变量。不同的是我们这里用的是一个Object类型接收,这点我想应该大家都是比较清楚的,因为java设计者设计这个方法时,他也不知道你现在的类的类型,而object类显然就没有这个接收问题(所有类的基类)。
Method[] methods= aClass.getDeclaredMethods();
for (int i = 0; i <methods.length ; i++) {
methods[i].invoke(o);
}
最后,这个代码应该是比较清楚的,调用getDeclaredMethods()方法,返回包含一个数组方法
对象反射的类或接口的所有声明的方法。特别要注意的是,这里的方法它也是一个对象(这可不是我乱说的,是jdk和设计反射的人说的),还真就万物皆对象啦。这个方法执行后,我们就得到了一个方法数组,一开始可能不太好理解,这个得自己多看,多尝试啦。
还有一点,可能有些同学发现我这里使用的方法是getDeclaredMethods(),而自己经常看到的是getMethods()这个方法,这里我说明一下,这两个方法的区别,getDeclaredMethods()返回的是表示此类
对象,包括公共,保护,默认访问和私有方法,但不包括继承的方法,也就是本类的所以方法,不包括父类。与之相反,getMethods()返回的是包括那些由类或接口和那些从超类和超接口继承的声明,只能是公共的方法。
methods[i].invoke(o);
这个方法在文档当中的解释是,在具有指定参数的方法对象上调用此方法
对象表示的基础方法。比较有趣的是,这里是在方法当中调用对象来执行方法。这个我就不细说了,有关反射的更详细的我会在以后出的。
3.反射的应用场合
当我们在编译时根本无法知道该对象或类可能属于哪些类,程序只依靠运行时信息来发现该对象和类的真实信息时,我们就可以使用反射机制来获取。
我们使用的一些框架底层,就运用到了反射机制,比如在spring中,我们将所有的类Bean交给spring容器管理,无论是XML配置Bean还是注解配置,当我们从容器中获取Bean来依赖注入时,容器会读取配置,而配置中给的就是类的信息,spring根据这些信息,需要创建那些Bean,spring就动态的创建这些类。还有在struts2的struts.xml中配置action,也是通过反射调用的action。还有我们经常使用的数据库的连接的配置也是这样。