##什么是反射
- 反射是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支持注解 - 强行执行私有方法,访问私有属性
反射 自己写一个注解
实现效果:输入一个类名,可以根据输入的类名,获取类中注解了@Demoo的所有方法
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Demoo
{
}
public class ReflectTest {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
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);
}
}
}
public class Star {
@Demoo
public void flicker(){
}
public void flashing(){
}
}
对于@Retention做一定解释,java文件被编译成class文件的时候,注解会被自动擦除,这是注解的默认在源文件中存在,就像这样:
@Retention(RetentionPolicy.SOURCE)
需要修改为:
@Retention(RetentionPolicy.CLASS)
不过这个时候,class进入代码区,注解还是没了,这个时候就需要:
@Retention(RetentionPolicy.RUNTIME)
这个注解不仅保证让其保存到class文件中,jvm加载class文件之后,仍然存在
而对于@Target注解,可以理解为将来这个注解你想放在哪个位置,我们要实现的效果是用在方法上,所以使用的是@Target(ElementType.METHOD)
- CONSTRUCTOR:用于描述构造器
- FIELD:用于描述域
- LOCAL_VARIABLE:用于描述局部变量
- METHOD:用于描述方法
- PACKAGE:用于描述包
- PARAMETER:用于描述参数
- TYPE:用于描述类、接口(包括注解类型) 或enum声明
来看看执行效果:
- 输入:mackyhuang.reflectTest.Star
(这里的输入需要类的全限定名,右击类,选择copy Reference即可) - 输出:
- public void huangzhenxin.reflectTest.Star.flicker() @huangzhenxin.reflectTest.Demoo()
- public void huangzhenxin.reflectTest.Star.flashing() null
- 成功扫描类的2个方法,并且找到注解的情况