反射
在运行时动态地获取一个类的信息并操作该类的属性和方法的技术
可以解决一些常规编程中难以预知类的情况下的问题
eg:
实例化一个类:通过反射可以根据类名动态实例化一个对象
调用类的方法:通过反射可以根据方法名动态地调用一个类的方法
获取和设置类的字段:通过反射可以动态获取并操作一个类的资源,无视权限修饰符
反射提供了一种在编译时无法确定类信息的情况下,动态获取和操作类的能力
使得程序在运行时可以根据不同的情况来选择执行不同的方法,使程序更加灵活和可扩展.
反射应用-获取类的字节码对象
反射的步骤
1,加载类,获取类的字节码文件:Class对象
2,获取类的构造器:Constructor对象
3,获取类的成员变量:Field对象
4,获取类成员方法:Method对象
获取类的字节码对象方式
同一个类的字节码对象相同
推荐使用类的静态属性class获取类的字节码对象
class.forName()适合用于需要动态加载类的情况
getClass()较少使用
1,使用类的静态属性Class
Class<?> clazz = MyClass.class;
2,使用Object类的getClass()方法
Class<?> clazz = myObject.getClass();
3,使用Class.forName()方法
Class<?> clazz = Class.forName("com.example")
反射的应用
Field[] aClass.getDeclaredFields()
//获取字节码对象的全部属性
构造器对象构造
Constructor<?>[] getConstructors()
//获取全部构造器(只能获取public修饰的)
//返回构造器对象数组
Constructor<?>[] getDeclaredConstructors()
//获取全部构造器(只要存在就能拿到)
//返回构造器对象数组
Constructor<T> getConstructor(Class<?>...parameterTypes)
//获取某个构造器(只能获取public修饰的)
eg:.getConstructor(int.class,String.class)
Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes)
//获取某个构造器(只要存在就能拿到)
获取类构造器的作用:初始化对象返回
T newInstance(Object.....initargs)
//调用此构造器对象表示的构造器,并传入参数,完成对象的初始化并返回
//返回一个对象
public void setAccessible(boolean flag)
//设置为true,表示禁止检查访问控制(暴力反射)
//取消权限检查
//构造器对象.setAccessible(true)
public String getName()
//返回带包类名
public String getSimpleName()
//返回类名
成员变量对象构造
Field getField(String name)
//返回类型中指定名称的public成员变量,name为变量名称
Field[] getFields
//返回该类型的所有public属性
Field getDeclaredField(String name)
//返回该类型中指定名称的属性,和属性权限无关
Field[] getDeclaredFields()
//返回该类型中的全部属性,与属性的访问级别无关
public String getName()
//返回属性名
public int getModifiers()
//获取属性的修饰符列表,返回的是数字,配合Modifier类的toString(int x)方法使用可以得到修饰符
public Class<?> getType()
//以Class类型,返回属性类型
配合Class类的getSimpleName()方法使用
public void set(Object obj,Object value)
//设置属性
public Object get(Object obj)
//读取属性值
方法对象构造
Method getMethod(String name,Class..params)
//返回实例中指定的public方法,name指定方法名称,params指定参数列表
Method[] getMethods()
//返回该实例中所有的public方法
Method getDeclaredMethod(String name,Class..params)
//返回实例中指定的方法,和权限修饰符无关
Method[] getDeclaredMethods()
//返回实例中全部的方法,和权限修饰符无关
Method方法
public String getName()
//返回方法名
public int getModifiers()
//获取方法修饰符列表
public Class<?> getReturnType()
//以Class类型,返回方法类型
配合Class类的getSimpleName()方法使用
public Class<?>[] getParameterTypes()
//返回方法的参数列表
public Object invoke(Object obj,Object...args)
//调用方法
注解
一种特殊的标记,可以被加在代码的某个地方,给代码提供额外的信息
可以用来帮助编译器,工具或者程序本身做一些特殊的处理.
使用场景:
1,编译时检查:帮助开发人员在编译期间发现潜在的错误,并提高了代码的可读性和可维护性
比如@Override
2,运行时处理:通过使用反射机制,可以在运行时获取和处理注解信息,例如通过注解实现自定义的依赖注入,AOP等功能
3,测试框架:使用注解来标记测试方法,测试类等,以便测试框架能够自动识别并执行测试
@Test
自定义注解:
1,使用@interface关键字定义一个注解类型
public @interface A{String a();int b() default 100;}@A
2,定义注解的属性:在注解类型中可以定义多个属性,用于存储注解的参数值.
类型可以是:基本数据类型,包装类,字符串类型,枚举类型,Class类型,注解类型和上述类型的数组
3,使用注解:在需要标记的代码元素上使用自定义注解.注解可以用于类,方法,字段等地方.可以为注解的属性赋予具体的值,或使用默认值.
注解参数的使用:注入代码元素
如果注解中只有一个属性需要赋值且这个属性叫value
在注解参数中可以只写value的值
@interface Test{String value();
}@Test("hello")
4,处理注解:如果需要对注解进行进一步处理,可以使用注解解析器.注解处理器可以在运行时扫描代码,提取注解信息并进行相应的处理.
通过自定义注解,我们可以在代码中通过注解来标记和传递额外的数据信息.
元注解
用来修饰其他注解的注解.
可以对注解进行更深入的描述和控制,提供了更多的灵活性和功能.
元注解的作用
对其他注解进行描述和控制.
告诉编译器和运行时环境如何处理注解
通过元注解,我们可以指定注解的生命周期(在源代码,编译后的class文件或运行时保留)
和作用目标(类,字段,方法等)
由java提供
常见元注解
@Target(ElementType.TYPE)
//声明注解使用的位置
1,TYPE,类,接口
2,FIELD,成员变量
3,METHOD,成员方法
4,PARAMETER,方法参数
5,CONSTRUCTOR,构造器
6,LOCAL_VARIBLE,局部变量
@Retention(RetentionPolicy.RUNTIME)
//声明注解的生命周期
1,SOURCE 只作用在源码阶段,字节码文件中不存在
2,CLASS(默认值) 保留到字节码文件阶段,运行阶段不存在
3,RUNTIME(开发常用) 一直保留到运行阶段
使用场景
@MyTest(value = "testClass",bbb={"b"})
class Demo{@MyTest(value = "testMethod",bbb={"a"})public void test(){}
}@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyTest{String value();double aaa() default 100;String[] bbb();
}
注解解析
是指对java程序中的注解进行解析和处理的过程
包含两个方面
注解的读取
通过反射机制获取程序中的注解信息.
通过反射可以获取到类,方法,字段等的注解,并可以获取注解中定义的属性值
和
注解的处理
根据注解的信息执行相应的操作
如果需要通过反射机制获取注解信息,注解的生命周期类型必须是RetentionPolicy.RUNTIME
解析注解的方法
先用反射获取需要解析的对象
public Annotation[] getDeclaredAnnotations()
//获取当前对象上面的注解
public T getDeclaredAnnotation(Class<T> annotationClass)
//获取指定的注解对象
public boolean isAnnotationPresent(Class<Annotation> annotationClass)
//判断当前对象上是否存在某个注解
Class,Method,Field,Constructor都实现了AnnotatedElement接口,都拥有解析注解的能力.
注解中的属性值即为解析出的注解对象属性值