前言:之前看spring IOC和AOP的实现原理时,一脸懵逼…随后了解到,spring 的IOC和AOP是基于Java反射机制和动态代理实现的时候,便回来恶补基础…
先科普下:
Reflection是Java被视为动态(或准动态)语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括modifiers(诸如public,static等等)、superclass(例如Object)、实现之interfaces(例如Serializable),也包括fields和methods的所有信息,并可于运行时改变fields内容或调用methods。通俗一点的(百度百科):JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
反射机制主要提供了以下功能:
1.在运行时判断任意一个对象所属的类;
2.在运行时构造任意一个类的对象;
3.在运行时判断任意一个类所具有的成员变量和方法;
4.在运行时调用任意一个对象的方法;
5.生成动态代理。
如果让我没了解反射之前看引用中的话,估计我也是有点懵逼的。我也是看了一遍慕课的反射-Java高级开发必须懂的 又看了一遍Java反射机制详解 终于大概懂了…
不扯了…
首先,我们需要了解的是“编译时”和”运行时”的不同,以new创建对象为例,
User w = new User();静态加载
new创建对象是静态加载类,在编译时进行加载,如果程序中其他的类有问题,那么没有问题的类也是无法执行的。
ps:功能性的类最好使用动态加载,而不是静态加载。动态加载类指的是程序运行时加载类,而静态加载指的是编译时加载类,编译时加载的缺点是程序中所有的功能都不能有差错,否则的话程序就不能用了,而动态加载类的好处就是我们需要使用哪一个类虚拟机就会动态加载根据我们的需要动态的加载这个类,这样程序的使用就不会受到其他的影响
解决这个问题可以使用动态加载(三种方法):
class demo1= Class.forName("包名+类名");
class demo2 = new 类名().getClass();
class demo3 = 类名.class;
其中demo1,demo2,demo3叫做类的类类型,通过类的类类型我们可以干很多事,例如:
通过类的类类型,我们可以
1. 获取它的父类与实现的接口
2. 实例化这个类的对象
3. 获取这个类的全部属性
4. 获取这个类的全部方法
5. 调用这个类的方法
6. 操作这个类的属性
…
总的意思就是,得到类的类类型,那么类的信息我们就能轻而易举的获得到了。★,°:.☆( ̄▽ ̄)/$:.°为所欲为★ 。
具体的实现?我就不造轮子了,下面这个链接里面的例子都挺不错的,讲的很全面
http://www.cnblogs.com/lzq198754/p/5780331.html
最后,需要明白的是反射机制是在编译后的,所以可以绕过编译
以泛型为例:
编译之后集合的泛型是去泛型化的
Java中集合的泛型,是防止错误输入的,只在编译阶段有效,
绕过编译就无效了
ArrayList list = new ArrayList();
ArrayList list1 = new ArrayList();
list1.add("hello");
//list1.add(20);错误的
Class c1 = list.getClass();
Class c2 = list1.getClass();
System.out.println(c1 == c2);
//反射的操作都是编译之后的操作
/*
* c1==c2结果返回true说明编译之后集合的泛型是去泛型化的
* Java中集合的泛型,是防止错误输入的,只在编译阶段有效,
* 绕过编译就无效了
* 验证:我们可以通过方法的反射来操作,绕过编译
*/
try {
Method m = c2.getMethod("add", Object.class);
m.invoke(list1, 20);//绕过编译操作就绕过了泛型
System.out.println(list1.size());
System.out.println(list1);
} catch (Exception e) {
e.printStackTrace();
}
上面这段代码中,我们通过m.invoke(list1, 20);
将一个int值写入到了list1中,而list1是一个String泛型,由此证明通过方法的反射来操作,可以绕过编译。