什么是反射
简而言之,通过反射,我们可以在运行时获得程序中每一个类型的成员和成员的信息。
程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。
所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。
反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。
Java 反射主要提供以下功能
- 在运行时构造任意一个类的对象。
- 在运行时调用任意一个对象的方法。
- 在运行时判断任意一个对象所属的类。
- 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法)。
反射的主要用途
反射最重要的用途就是开发各种通用框架。
很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 Bean),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。
举一个例子,在运用 Struts 2 框架的开发中我们一般会在 struts.xml 里去配置 Action,比如:
1
2
3
4
| <action name="login" class="org.ScZyhSoft.test.action.SimpleLoginAction" method="execute">
<result>/shop/shop-index.jsp</result>
<result name="error">login.jsp</result>
</action>
|
配置文件与 Action 建立了一种映射关系,当 View 层发出请求时,请求会被 StrutsPrepareAndExecuteFilter 拦截,然后 StrutsPrepareAndExecuteFilter 会去动态地创建 Action 实例。比如我们请求 login.action,那么 StrutsPrepareAndExecuteFilter就会去解析struts.xml文件,检索action中name为login的Action,并根据class属性创建SimpleLoginAction实例,并用invoke方法来调用execute方法,这个过程离不开反射。
对与框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的核心。而对于一般的开发者来说,不深入框架开发则用反射用的就会少一点,不过了解一下框架的底层机制有助于丰富自己的编程思想,也是很有益的。
像Java中的一大利器注解的实现也用到了反射。
为什么你使用 Spring 的时候 ,一个@Component注解就声明了一个类为 Spring Bean 呢?为什么你通过一个 @Value注解就读取到配置文件中的值呢?究竟是怎么起作用的呢?
这些都是因为你可以基于反射分析类,然后获取到类/属性/方法/方法的参数上的注解。你获取到注解之后,就可以做进一步的处理。
反射的基本运用
获得Class对象
使用Class类的forName 静态方法。
1
| Class appleClass = Class.forName("base.reflection.Apple");
|
直接获取
1
| Class appleClass = Apple.class;
|
获取某一个对象的class
1
2
| Apple apple = new Apple();
Class appleClass = apple.getClass();
|
通过类加载器ClassLoader.loadClass()传入类路径获取
1
| Class appleClass = ClassLoader.getSystemClassLoader().loadClass("base.reflection.Apple");
|
构造方法
1
2
3
4
5
6
7
| Constructor[] declaredConstructors = appleClass.getDeclaredConstructors();
Constructor[] constructors = appleClass.getConstructors();
//通过无参构造来获取该类对象 newInstance()
Apple apple= (Apple)appleClass.newInstance();
//通过有参构造来获取该类对象 newInstance
Constructor constructor = appleClass.getConstructor(String.class,int.class,int.class);
Apple apple=(Apple)constructor.newInstance("红色",10,5);
|
属性
1
2
3
4
| //getDeclaredFields所有已声明的成员变量,但不能得到其父类的成员变量
Field[] declaredFields = appleClass.getDeclaredFields();
//getFields访问公有的成员变量
Field[] fields = appleClass.getFields();
|
方法
1
2
3
4
| //getDeclaredMethods 方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
Method[] declaredMethods = appleClass.getDeclaredMethods();
//getMethods方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。
Method[] methods = appleClass.getMethods();
|
调用方法
1
2
3
4
5
6
| Constructor constructor = appleClass.getConstructor(String.class,int.class,int.class);
Apple apple = (Apple)constructor.newInstance("红色",10,5);
//获取toString方法并调用
Method method = appleClass.getDeclaredMethod("toString");
String str = (String)method.invoke(apple);
System.out.println(str);
|
利用反射创建数组
1
2
3
4
5
6
7
8
9
10
| Class<?> cls = Class.forName("java.lang.String");
Object array = Array.newInstance(cls,5);
//往数组里添加内容
Array.set(array,0,"hello");
Array.set(array,1,"Java");
Array.set(array,2,"fuck");
Array.set(array,3,"Scala");
Array.set(array,4,"Clojure");
//获取某一项的内容
System.out.println(Array.get(array,3));
|