注解(Annotation)
文章目录
一、注解概述
从 JDK 5 开始,Java增加了对元数据(MetaData)的支持,也就死Annotation(即注解)。注解其实是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。
注解提供了一种为程序元素设置元数据的方法,注解就像修饰符一样,可用于修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被存储在注解的“name = value”中。
二、注解的作用
- 编写文档: 通过代码里标识的元数据生成文档【生成文档doc文档】
- 代码分析: 通过代码里标识的元数据对代码进行分析【使用反射】
- 编译检查: 通过代码里标识的元数据让编译器能够实现基本的编译检查【Override等】
编写文档
首先,我们要知道Java中是有三种注释的,分别为单行注释、多行注释和文档注释。而文档注释中,也有@开头的元注解,这就是基于文档注释的注解。我们可以使用javadoc命令来生成doc文档,此时我们文档的内元注解也会生成对应的文档内容。这就是编写文档的作用。
代码分析
我们频繁使用之一,也是包括使用反射来通过代码里标识的元数据对代码进行分析的,此内容我们在后续展开讲解。
编译检查
至于在编译期间在代码中标识的注解,可以用来做特定的编译检查,它可以在编译期间就检查出“你是否按规定办事”,如果不按照注解规定办事的话,就会在编译期间飘红报错,并予以提示信息。可以就可以为我们代码提供了一种规范制约,避免我们后续在代码中处理太多的代码以及功能的规范。比如,@Override注解是在我们覆盖父类(父接口)方法时出现的,这证明我们覆盖方法是继承于父类(父接口)的方法,如果该方法稍加改变就会报错;@FunctionInterface注解是在编译期检查是否是函数式接口的,如果不遵循它的规范,同样也会报错。
三、常见基本注解
- @Override: 标记在成员方法上,用于标识当前方法是重写父类(父接口)方法,编译器在对该方法进行编译时会检查是否符合重写规则,如果不符合,编译报错。
- @Deprecated: 用于标记当前类、成员变量、成员方法或者构造方法过时如果开发者调用了被标记为过时的方法,编译器在编译期进行警告。
- @SuppressWarnings: 压制警告注解,可放置在类和方法上,该注解的作用是阻止编译器发出某些警告信息。
四、元注解
元注解是修饰其他注解的注解。
常用的4个元注解:@Retention、@Target、@Document、@Inherit。
@Retention
@Retention只能用于修饰注解定义,用于指定被修饰的注解可以保留多长时间。
@Retention包含一个RetentionPolicy类型的value成员变量,所以使用@Retention时必须为该value成员变量指定值,value的值只能是如下三个:
RetentionPolicy.CLASS
:编译器将把注解记录在class文件中。当运行Java程序时,JVM不可获取注解信息。(默认)。RetentionPolicy.RUNTIME
:编译器将把注解记录在class文件中。当运行Java程序时,JVM可以获取注解信息,程序可以通过反射获取该注解信息。RetentionPolicy.SOURCE
:注解只保留在源代码中,编译器直接丢弃这种注解。
如果需要通过反射获取注解信息,需要将value属性值设置为RetentionPolicy.RUNTIME
。
@Target
@Target也只能修饰注解定义,它用于指定被修饰的注解能用于哪些程序单元。
@Target元注解也包含一个value成员变量,value的值只能是如下几个:
ElementType.ANNOTATION_TYPE
:修饰注解。ElementType.CONSTRUCTOR
:修饰构造器。ElementType.FIELD
:修饰成员变量。ElementType.LOCAL_VARIABLE
:修饰局部变量。ElementType.METHOD
:修饰方法。ElementType.PACKAGE
:修饰包。ElementType.PARAMETER
:修饰参数。ElementType.TYPE
:修饰类、接口(包括注解类型)、枚举。
@Document
@Document用于指定被元注解修饰的注解类将被javadoc工具提取成文档。
自定义一个TestDocumentAnnotation
注解:
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface TestDocumentAnnotation {
}
编写一个TestDocument
测试类:
public class TestDocument {
@TestDocumentAnnotation
public void info() {
System.out.println("TestDocumentAnnotation");
}
}
生成的JavaDoc:
@Inherited
@Inherited元注解指定被它修饰的注解将具有继承性--------如果某个类使用了@Xxx注解(定义该注解时使用了@Inherited修饰)修饰,则其子类将自动被@Xxx修饰。
五、自定义注解
定义注解
自定义注解使用关键字@interface
。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) // 使用@Retention指定注解保留到运行时
@Target(ElementType.METHOD) // @Target指定注解可用于修饰方法
public @interface MyAnnotation {
// 注解中的成员变量以方法的形式来定义
// default为两个成员变量指定默认初始值
String var1() default "gengduc";
int var2() default 21;
}
如果为注解的成员变量指定了默认值,使用该注解时则可以不为这些成员变量指定值,而是直接使用默认值。
提取注解信息
public class MyClass {
@MyAnnotation
public void info1() {
System.out.println("info1...");
}
@MyAnnotation(var1 = "CGengdu", var2 = 22)
public void info2() {
System.out.println("info2...");
}
}
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class TestMyAnnotation {
public static void main(String[] args) throws ClassNotFoundException {
Class<?> clazz = Class.forName("MyClass");
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println("Method = " + method);
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("\t Annotation = " + annotation);
}
}
}
}
Method = public void MyClass.info1()
Annotation = @MyAnnotation(var2=21, var1=gengduc)
Method = public void MyClass.info2()
Annotation = @MyAnnotation(var2=22, var1=CGengdu)