如果还不了解Java反射,来入个门,再自定义一个注解

257 阅读4分钟

##什么是反射

  • 反射是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)

  1. CONSTRUCTOR:用于描述构造器
  2. FIELD:用于描述域
  3. LOCAL_VARIABLE:用于描述局部变量
  4. METHOD:用于描述方法
  5. PACKAGE:用于描述包
  6. PARAMETER:用于描述参数
  7. TYPE:用于描述类、接口(包括注解类型) 或enum声明

来看看执行效果:

  • 输入:mackyhuang.reflectTest.Star
    (这里的输入需要类的全限定名,右击类,选择copy Reference即可)
  • 输出:
    • public void huangzhenxin.reflectTest.Star.flicker() @huangzhenxin.reflectTest.Demoo()
    • public void huangzhenxin.reflectTest.Star.flashing() null
  • 成功扫描类的2个方法,并且找到注解的情况

博文是作者原本在其他平台的,现迁移过来