目录
基本概念
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
作用分类
- 编写文档:通过代码里标识的注解生成文档【生成文档doc文档】
- 代码分析:通过代码里标识的注解对代码进行分析【使用反射】
- 编译检查:通过代码里标识的注解让编译器能够实现基本的编译检查【Override】
JDK基本注解
JDK中提供了5个基本注解用法
- @Override :检测被该注解标注的方法是否是继承自父类(接口)的
- @Deprecated:该注解标注的内容,表示已过时
- @SuppressWarnings:压制警告,一般传递参数all @SuppressWarnings("all")
- @SafeVarargs: 处理可变长参数中的泛型,此注解告诉编译器:在可变长参数中的泛型是类型安全的
- @FunctionalInterface: 指定的接口是函数式接口
其中@SafeVarargs是Java7中新增的,@FunctionalInterface是Java8中新增的。
元注解
JDK除了在java.lang下提供了5个基本注解之外,还在java.lang.annotation包下提供了6个Meta注解(元注解),其中有5个元注解都是用于修饰其他的注解的定义,其中@Repeatable专门用于定义Java8新增的重复注解。这里重点介绍4个常用的元注解。
@Retention
Retention只能用于修饰注释定义,用于指定被修饰的注解可以保留多长时间,@Retention包含一个RetentionPolicy类型的value成员变量,使用@Retention时必须为该value成员指定值,value成员变量的值只能是如下三个。
- RetentionPolicy.SOURCE: 编译器把注解记录在class文件中,当运行Java程序时,JVM不可获取注解信息,这是默认值。
- RetentionPolicy.CLASS:编译器把注解记录在class文件中,当运行Java程序时,JVM也可获取注解信息,程序可以通过反射获取该注解信息
- RetentionPolicy.RUNTIME:注解值保留再源代码中,编译器直接丢弃这种注解
demo:
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Testable {
String value();
int[] age() default {1,2};
}
当注解中属性值为value时,无需使用name=value的方式,可直接赋值
@Retention(RetentionPolicy.RUNTIME)
public @interface Testable {
String value();
int[] age() default {1,2};
}
@Target
@Target也只能修饰注解定义,它用于指定被修饰的注解能用于修饰哪些程序单元,@Target元注解也包含一个名为value的成员变量,该成员变量的值只能是如下几个
@Target(ElementType.TYPE)
——修饰接口、类、枚举、注解@Target(ElementType.FIELD)
——修饰字段、枚举的常量@Target(ElementType.METHOD)
——修饰方法@Target(ElementType.PARAMETER)
——修饰方法参数@Target(ElementType.CONSTRUCTOR)
——修饰构造函数@Target(ElementType.LOCAL_VARIABLE)
——修饰局部变量@Target(ElementType.ANNOTATION_TYPE)
——修饰注解@Target(ElementType.PACKAGE)
——修饰包
@Document
@Document用于指定被该元注解修饰的注解类将被javadoc工具提取成文档,如果定义注解类时使用了@Document修饰,则所有使用该注解修饰的程序元素的API文档中将会包含该注解。
@Inherited
@Inherited元注解指定被它修饰的注解将具有继承性,---如果某个类使用了@Xxx(定了该注解是使用了@inherited修饰)修饰,则其子类将自动被@Xxx修饰
demo
// 注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Testable {
String value();
int[] age() default {1,2};
}
//父类
@Testable(value = "1")
public class Test {
public static void main(String[] args) {
Class<Test> testClass = Test.class;
boolean annotationPresent = testClass.isAnnotationPresent(Testable.class);
System.out.println(annotationPresent);
}
}
输出:
true
//子类
public class SubTest extends Test {
public static void main(String[] args) {
Class<SubTest> subTestClass = SubTest.class;
boolean annotationPresent = subTestClass.isAnnotationPresent(Testable.class);
System.out.println(annotationPresent);
}
}
输出:
false
当给@Testable加上@Inherited时,可以看到SubTest继承了Test的注解
自定义注解
自定义注解使用@interface关键字,与定义一个接口非常像
public @interface Test{
}
内部属性值要求:
- 基本数据类型
- String
- 枚举
- 注解
- 以上类型的数组
定义了该注解后,就可以在程序中的任何地方使用该注解。
提取注解信息
// 定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Inherited
public @interface Testable {
String value();
int[] age() default {1,2};
}
@Testable(value = "Test")
public class Test {
@Testable("run1")
public void run1() {
}
@Testable("run2")
public void run2() {
}
public void run3() {
}
public static void main(String[] args) throws ClassNotFoundException {
Class<Test> testClass = Test.class;
// 判断该类是否存在Testable注解
boolean annotationPresent = testClass.isAnnotationPresent(Testable.class);
System.out.println("testClass is AnnotationPresent===>" + annotationPresent);
Method[] methods = testClass.getMethods();
for (Method method : methods) {
// 判断当前方法是否包含Testable注解
if (method.isAnnotationPresent(Testable.class)) {
//提取当前方法上的注解
Testable annotation = method.getAnnotation(Testable.class);
System.out.println(method.getName() + "===age==>" + annotation.age());
System.out.println(method.getName() + "===value==>" + annotation.value());
}
}
}
}
输出
注意:
1. 如果定义属性时,使用default关键字给属性默认初始化值,则使用注解时,可以不进行属性的赋值。
2. 如果只有一个属性需要赋值,并且属性的名称是value,则value可以省略,直接定义值即可。
3. 数组赋值时,值使用{}包裹。如果数组中只有一个值,则{}可以省略