反射
@author:MackyHuang
什么是反射
- 反射是JAVA API, 是Java提供的现成的类,接受API提供的内容
- 是Java提供的动态执行机制,动态加载类,动态创建对象,动态访问属性,动态调用方法
静态和动态
- 静态:事先约定好的规则,执行期间按照固定的规则执行
- 动态:事先没有约定规则,在执行期间动态确定执行规则
动态加载类
Java提供了动态加载类的API
Class cls = Class.forName("")
引号里面放的是类名
一个类,被编译成
.class
文件,类加载器读取后存在于方法区中,java反射机制分析方法区中的字节码文件,输入className后,在堆中的Class去方法中找到相应的类的代码(类的属性和方法都在其中),然后在栈中创建引用,指向Class,这样就获得了一个cls->Class>具体类的过程Class cls = Class.forName(className); Object object = cls.newInstance();
这里通过newInstance()
方法获取这个对象的实例,这里需要这个类提供无参构造器
com.cls.Test@74a14482
这个@后面的数字和地址有关系,但是绝对不是地址!
动态获取类的方法信息
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods)
{
System.out.println(method);
}
通过getDeclaredMethods()
来获取方法信息,从方法区中抓取信息 ,返回的是一个Method[]
数组,数组中是这样的字符串public int com.cls.Test.name2()
- 这里Method还有一些方法来获取方法信息的局部信息,比如获取方法名之类的
动态执行方法
method.invoke(对应的类的对象,参数1, 参数2);
因为在函数运行的时候,有一个隐藏参数就是this,在这里就相当于这个this,需要传入的参数也就是这个类的对象,然后接着输入参数,这样可以运行类的非静态方法,invoke有返回值,返回的是方法返回的值
invoke执行私有方法
Class[] types = {String.class, int.class};
Method method = cls.getDeclaredMethod("name3", types);
method.setAccessible(true);
通过类型列表和getDeclaredMethod
方法获取一个方法,
正常情况下,invoke无法访问私有方法,如果method获得打开访问权限,将setAccessible
设置为true,就可以设置其访问私有属性,不过这样会破坏封装性(必要的时候再用)
反射的用途
- eclipse 中解析类的结构使用了反射
- Junit识别被测试方法使用了反射
Junit3利用反射查找test开头的方法
Junit4利用反射解析@Test查找测试方法 - Spring管理Bean对象,注入Bean属性使用了反射
- 注解的解析使用了反射
利用反射API支持注解 - 强行执行私有方法,访问私有属性
反射自己写一个注解
String className = "";
System.out.println("请输入类名");
Scanner input = new Scanner(System.in);
className = input.nextLine();
Class cls = Class.forName(className);
Object object = cls.newInstance();
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods)
{
Demoo demoo = method.getAnnotation(Demoo.class);
System.out.println(method + " " + demoo);
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Demoo
{
}
以为这样就可以抓取到注解了吗,大错特错!
java文件被编译成class文件的时候,注解会被自动擦除,这是注解的默认在源文件中存在,就像这样:
@Retention(RetentionPolicy.SOURCE)
需要修改为:
@Retention(RetentionPolicy.CLASS)
不过这个时候,class进入代码区,注解还是没了,这个时候就需要:
@Retention(RetentionPolicy.RUNTIME)