第一次接触到反射的时候,还不太明白反射的原理的作用,直到自己做的一个项目用到反射,才明白反射的强大。因此,写一篇博客对其进行总结。
一. 反射的定义
正常情况下,如果我们已经有一个类,则肯定可以通过类创建对象,如果现在想通过一个对象找到一个类的名称,这个时候就需要用到反射机制。更理论的说法是,Java的反射机制可以让我们在Java的运行环境中,知道任意对象的属性,也可以调用他的方法。刚开始可能有点难以理解,还是通过几个例子来理解什么是反射。
二. 认识Class类
在学习反射之前,不得不对Class类有一定的了解。在正常情况下,需要先有一个类的完整路径引入之后才可以按照固定的格式产生实例,但是在Java中也允许通过一个实例化的对象找到一个类的完整信息,这就是Class类的功能。
1: package com.wbl.reflect.demo
2: class X{}
3: public class GetClassDemo{
4: public static void main(String[] args){
5: X x = new X();
6: System.out.println(x.getClass().getName());
7: }
8: }
运行以上的程序,会输出“com.wbl.reflect.demo.X”。这样我们就通过一个对象得到了对象所在的完整“包.类”名称。其中getClass()方法是从Object类中继承而来的,它是返回值类型就是一个Class类,实际上此类就是Java反射的源头。
Class表示一个类的本身,通过Class可以完整地得到一个类中的完整结构,包括此类中方法定义,属性定义等。
Class类中常用的方法
方法 | 描述 |
public static Class<?> forName(String className) | 传入完整的“包.类”名称实例化对象 |
public Constructor[] getConstructors() | 得到一个类中全部的构造方法 |
public Filed[] getDeclaredFields() | 得到本类中单独定义的全部属性 |
public Filed[] getFields() | 得到本类继承而来的全部属性 |
public Method[] getMethods() | 得到一个类中全部的方法 |
public Method getMethod(String name, Class…parameter) | 返回一个Method对象,并设置该方法中的所有参数 |
public String getName() | 得到一个类的完整的“包.类”的名称 |
三. 反射的应用——得到类的结构
(1) 取得类中所有的构造方法
在下面的例子,我们得到了String类的全部的构造方法
1: package com.wbl.test;
2:
3: import java.lang.reflect.Constructor;
4:
5: /**
6: * Created by Lulala on 2015/7/12.
7: */
8: public class ReflectDemo {
9: public static void main(String[] args){
10: Class c = null;
11: try {
12: c = Class.forName("java.lang.String"); //实例化Class对象
13: }catch (ClassNotFoundException e){
14:
15: }
16: Constructor<?> con[] = c.getConstructors(); //得到全部的构造方法
17: for(int i = 0; i < con.length; i++)
18: System.out.println(con[i]);
19: }
20: }
(2) 取得全部的方法
要取得一个类中全部的方法,可以使用Class类中的getMethods()方法,此方法返回一个Method对象类的对象数组。通过Method类,我们可以得到方法的参数,访问修饰符(public private..),抛出的异常等
Method类中常用的方法
方法 | 描述 |
public int getModifiers() | 取得本方法的访问修饰符 |
public String getName() | 取得方法的名称 |
public Class<?> getParameterTypes() | 得到方法全部参数类型 |
public Class<?> getReturnTpyes() | 得到方法的返回值类型 |
public Class<?> getExceptionTypes() | 得到一个方法的全部异常 |
public Object invoke(Object obj,Object…args) | 通过反射调用类中的方法 |
1: public static void getMethodInfo(String className){
2: Class c = null;
3: try {
4: c = Class.forName(className);
5: }catch (ClassNotFoundException e){
6: e.printStackTrace();
7: }
8: Method method[] = c.getMethods();
9: for (int i =0; i < method.length; i++){
10: Class<?> returnType = method[i].getReturnType(); // 取得方法的返回值类型
11: Class<?> parameter[] = method[i].getParameterTypes(); //取得全部的参数类型
12: int mod = method[i].getModifiers(); //得到方法的访问修饰符
13: System.out.print(Modifier.toString(mod) + " "); //还原修饰符
14: System.out.print(returnType.getName() + " ");
15: System.out.print(method[i].getName());
16: System.out.print("(");
17: for (int j = 0 ; j < parameter.length; j++){
18: System.out.print(parameter[j].getName() + " " + "arg" + j);
19: if(j < parameter.length - 1)
20: System.out.print(",");
21: }
22: System.out.println(")");
23: }
24: }
输出结果:
(3) 取得全部的属性
在反射中取得类中的全部属性,有两种不同的操作
1.得到实现的接口或者类中的公共属性:public Field[] getFields()
2.得到本类中的全部属性:public Field[] getDeclareFields()
方法 | 描述 |
public Object get(Object obj) | 得到一个对象属性中的内容 |
public void set(Object obj) | 设定指定对象中属性的内容 |
public int getModifiers() | 得到属性的访问修饰符 |
public String getNames() | 返回此属性的名称 |
1: public static void getFieldInfo(String className){
2: Class c = null;
3: try {
4: c = Class.forName(className);
5: }catch (ClassNotFoundException e){
6: e.printStackTrace();
7: }
8: Field[] fields = c.getDeclaredFields();
9: for(int i = 0 ; i < fields.length; i++){
10: Class<?> returnType = fields[i].getType();
11: int mod = fields[i].getModifiers();
12: System.out.print(Modifier.toString(mod) + " ");
13: System.out.print(returnType.getName() + " ");
14: System.out.print(fields[i].getName());
15: System.out.println(";");
16: }
17: }
输出结果:
四. 通过反射调用类中的方法
(1) 调用setter方法
1: /**
2: * @param object 操作的对象
3: * @param att 操作的属性
4: * @param value 设置的值
5: * @param type 参数类型
6: * */
7: public static void setter(Object object, String att, Object value, Class<?> type){
8: try {
9: Method method = object.getClass().getMethod("set" + initStr(att),type);
10: method.invoke(object,value);
11: } catch (NoSuchMethodException e) {
12: e.printStackTrace();
13: } catch (InvocationTargetException e) {
14: e.printStackTrace();
15: } catch (IllegalAccessException e) {
16: e.printStackTrace();
17: }
18: }
19: /**
20: * 单词首字母大写
21: */
22: public static String initStr(String str){
23: return str.substring(0,1).toUpperCase() + str.substring(1);
24: }
(2) 调用getter方法
1: /**
2: * @param object
3: * @param object 操作的对象
4: * @param att 操作的属性
5: * */
6: public static void getter(Object object, String att){
7: try {
8: Method method = object.getClass().getMethod("get" + initStr(att));
9: System.out.println(method.invoke(object));
10: } catch (NoSuchMethodException e) {
11: e.printStackTrace();
12: } catch (InvocationTargetException e) {
13: e.printStackTrace();
14: } catch (IllegalAccessException e) {
15: e.printStackTrace();
16: }
17: }
五. 反射的应用场景
1.可以利用反射实现动态代理,解决开发中代理类过多的问题,提供统一的代理实现。
2.反射可以应用于工场模式和单例模式
3.反射机制是框架技术的原理和核心部分,例如Spring。通过反射机制我们可以动态的通过改变配置文件(以后是XML文件)的方式来加载类、调用类方法,以及使用类属性。这样的话,对于编码和维护带来相当大的便利,实现代码与配置文件相分离。