JavaSE注解&反射
JavaSE注解&反射
SmartSage一、注解
1.注解的概念
在Java中注解其实就是写在接口、类、属性、方法上的一个标签,或者说是一个特殊形式的注释。但是,注释只是一个注释,而注解在代码运行时是可以被反射读取并进行相应的操作,而如果没有使用反射或者其他检查,那么注解是没有任何真实作用的,也不会影响到程序的正常运行结果。
2.注解的作用
- 作为特定标记,用于告诉编译器一些信息,比喻
@Override
- 编译时动态处理,如动态生成代码,比如lombok的
@Data
- 运行时动态处理,作为额外信息的载体,如获取注解信息
3.注解的分类
- 元注解 – 注解的注解,标明该注解的使用范围、生命周期等。
- 标准注解 – Java提供的一些注解,标明过期的元素/标明是复写父类方法的方法/标明抑制警告。
- 自定义注解 – 第三方定义的注解,含义和功能由第三方来定义和实现。
3.1元注解
用于定义注解的注解,通常用于注解的定义上,标明该注解的使用范围、生效范围等。
元注解有五种:@Retention、@Target、@Documented、@Inherited、@Repeatable
最常用的是@Retention、@Target
@Retention
标明自定义注解的生命周期
生命周期:源文件>Class文件 > 运行时数据 (SOURCE>CLASS>RUNTIME)
- SOURCE 源代码java文件,生成的class文件中就没有该信息了
- CLASS class文件中会保留注解,但是jvm加载运行时就没有了
- RUNTIME 运行时,如果想使用反射获取注解信息,则需要使用RUNTIME,反射是在运行阶段进行反射的
1 |
@Target
描述自定义注解的使用范围,允许自定义注解标注在哪些Java元素上(类、方法、属性、局部属性、参数…)
value是一个数组,可以有多个取值,说明同一个注解可以同时用于标注在不同的元素上。
value的取值:
值 | 说明 |
---|---|
TYPE | 类、接口、注解、枚举 |
FIELD | 属性 |
METHOD | 方法 |
PARAMETER | 方法参数 |
CONSTRUCTOR | 构造函数 |
LOCAL_VARIABLE | 局部变量(如循变量、catch参数) |
ANNOTATION_TYPE | 注解 |
PACKAGE | 包 |
TYPE_PARAMETER | 泛型参数 |
TYPE_USE | 任何元素 |
@Inherited
是否可以被标注类的子类继承。被@Inherited
修饰的注解是具有继承性的,在自定义的注解标注到某个类时,该类的子类会继承这个自定义注解。这里需要注意的是只有当子类继承父类的时候,注解才会被继承。类实现接口,或者接口继承接口,都是无法获得父接口上的注解声明的,这些需要通过反射来获得父类的注解。
@Repeatable
是否可以重复标注。这个注解其实是一个语法糖,标注的是多个@MyAnnotation,其实会给我们返回一个@MyAnnotations,相当于是Java帮我们把重复的注解放入了一个数组属性中,所以只是一个语法糖而已。
@Documented
是否在生成的JavaDoc文档中体现,被标注该注解后,生成的javadoc中,会包含该注解。
3.2 标准注解
@Override 标记一个方法是覆写父类方法
@Deprecated 标记一个元素为已过期,避免使用。支持的元素类型为:CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE
@SuppressWarnings 不输出对应的编译警告。
3.3自定义注解
格式
1 | public 注解名 { |
注解的本质就是一个接口,并且继承了java.lang.annotation.Annotation
,在程序运行时,JVM会为其生成对应的代理类。
返回值支持的类型:
- 基本类型 int float boolean byte double char logn short
- String
- Class
- Enum
- Annotation
- 以上所有类型的数组类型
1 | // 保留至运行时 |
二、反射
1.反射的概念
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法,这种动态获取、调用对象方法的功能称为java语言的反射机制。
通过一个类的对象,看到这个类的结构,形象的称之为反射
2.反射的功能
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解
生成动态代理
……
虽然反射可以实现动态创建对象和编译,非常灵活,但是是一种解释操作,对性能有影响
3.核心API
1 | java.lang.Class // 代表一个类 |
3.1 Class对象
通过反射获取Class对象
1 | Animal animal = new Animal(); |
注意
一个类在内存中只有一个Class对象
一个类被加载后,类的整个结构都会被封装在Class对象中,通过这个class对象,就可以获得这个类的所有内容
有Class对象的类型
class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
interface:接口
[]:数组
enum:枚举
annotation:注解@interface
primitive type:基本数据类型
void
void.class
3.2 Constructor
获取到Class对象后,我们就可以通过Class对象获取到该类的构造器了。
1 | //获得所有构造方法,返回Constructor对象数组 |
3.3 Field
1 | //获取属性 |
3.4 Method
1 | c1.getMethods(); // 获取所有public方法(包含父类的方法) |
3.5 Other
1 | //获取类名 |
4.创建对象
1 | // Class.newInstance()创建对象 |
加入构造器是private的,那么就不可以直接使用,需要用setAccessible()方法设置权限后在使用
setAccessible()
值为 true 则指示反射的对象(获取的Method,Field,Construct等)在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查;实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问为false就不能访问 ;比如原来是public,无论是true或者false都可以访问。
1 | Constructor constructor = c1.getDeclaredConstructor(null); |
5.调用方法
使用invoke()
来执行
1 | // 参数: |
注意:
如果底层方法是静态的,那么
obj
参数为null
如果底层方法所需的形参数为零个,则所提供的
args
数组长度可以为 0 或 null。如果底层方法是静态的,并且尚未初始化声明此方法的类,则会将其初始化。
如果方法正常完成,则将该方法返回的值返回给调用者;如果该值为基本类型,则首先适当地将其包装在对象中。但是,如果该值的类型为一组基本类型,则数组元素不 被包装在对象中;换句话说,将返回基本类型的数组。如果底层方法返回类型为 void,则该调用返回 null。
三、注解与反射
1 | //参数:要获取的注解的类型。 |