Java基础知识之——反射

来源:互联网 发布:海陆丰制毒 知乎 编辑:程序博客网 时间:2024/06/06 14:16

决定从现在开始养成写博客记录学习过程的习惯,所以把最近学的反射相关知识整理一下。

首先,什么是反射?

从概念上说,反射主要是指程序可以访问、检测和修改它自身状态或行为的一种能力。

而我的理解是:常规操作一个类需要实例化它的对象,然后操作它的数据成员或成员函数,在逻辑是是由上而下的;

   而反射不同,它是反过来获取类的类类型(Class Type),而后再进行操作,有一种由下而上的感觉。

区别在哪里呢?

打个比方,大多数包装好的类他们的对象都是一个宝库,但我们往往只会用到其中的一部分,由多变少;

而反射则是通过仅仅一个对象获取到整个类的全部信息,由少变多。

换个说法:对象照了下镜子,反射 出了它作为一个类的全貌。

同时,反射机制在java中是动态的,你完全可以写一个可能根本不存在的class交给编译器处理,而且能够通过编译。

即:程序运行时,允许改变程序结构或变量类型

java虽然一般归类为静态语言,但是它有反射这样一个动态的机制。

其次,反射具体是什么?

java里万事万物皆对象,这句话大家应该都知道,Object类是这句话的一个保证,它是java里所有类的父类。

Class类是Object的一个特别的子类,是一个描述类自身的类,也是反射(Reflection)的起源。

一个类应当有些什么呢?数据成员?成员函数?构造器?

Class类封装了描述数据成员的Field、描述方法的Method和描述构造器的Constructor等属性。

java.lang.Class

java.lang.reflect.Field

java.lang.reflect.Method

java.lang.reflect.Constructor

java.lang.reflect.Modifier

五个类的API中我们可以了解到反射的绝大多数操作方法。

再次,反射有什么用?

从最简单的开始,作为一个菜鸟程序员,我很好奇编写java的大牛是怎么定义String的

那么我首先要得到String类的类类型,有三种方法可以做到这一点:

//通过Class的forName方法Class c1 = Class.forName("String");//通过每个类的class成员获取Class c2 = String.class;//通过该类的一个对象获取Class c3 = new String().getClass();

“现在,我的手里掌握了String的全部。”

/* * 万事万物皆对象,数据成员当然也是变量,java.lang.reflect.Field类封装了关于成员变量的操作 * 想看String的数据成员?可以用getField方法 获取所有public的数据成员  * 但是往往private的东西是我们更想看的,所以用getDeclaredFields获取所有自己声明的数据成员 */Field[] fs = c.getDeclaredFields();//现在Field的数组fs中就包含了String类的所有数据成员的信息//通过getType获取它们的类型Class fieldType = field.getType(); //通过getType获取它们的名称Class fieldName = field.getName();/* * 或者也可以写一个遍历来输出它们的类型和名称 * for(Field field:fs){ *Class fieldType = field.getType();  *//输出成员变量的类型以及名称 *System.out.println(fieldType.getName()+" "+field.getName()); * } */

类似的,可以通过Method类等类封装的方法来操作该类的成员方法等,在此不再赘述。

最后,通过反射深入研究泛型。

随意声明一个只能限制String类型的ArrayList

ArrayList<String> list = new ArrayList<String>();//我们可以自由地往里面加字符串list.add("233");//但是不能往里加其他类型的对象,如int//list.add(233); 报错


让我们做个实验,再声明一个无限制的ArrayList

ArrayList arr = new ArrayList();//获取它们俩的类类型Class c1 = list.getClass();Class c2 = arr.getClass();//输出它们是否相同System.out.println(c1==c2);//结果为true(可自行验证)
事实上反射都是在编译后的操作(运行时处理), 即程序通过编译过后运行时泛型是不泛型的,java的泛型仅是防止错误输入。
仅在编译阶段有效 。
而反射获取的是.class,可以理解为获取了编译之后的字节码文件,也就是说:反射是在编译之后的操作(运行时处理)
这也反映了反射的动态性质,通过这个性质,我们可以绕过编译(为所欲为)。
try{//通过Method类的getMethod方法获取ArrayList的add方法Method m = c1.getMethod("add", Object.class);//再通过invoke方法,反向操作list对象,使其添加非String元素m.invoke(list, 233);//为所欲为!System.out.println(list);//但是不能通过for each来遍历现在的list,会有类型转换的错误} catch (Exception e) {e.printStackTrace();}



(水平尚浅,有何错误、不妥之处欢迎指教)