1 注解的定义和用途
1.1 官方描述
An annotation is a form of metadata, that can be added to Java source code.
Classes, methods, variables, parameters and packages may be annotated.
Annotations have no direct effect on the operation of the code they annotate.
- 注解是一种可以被添加到java代码中的元数据,属于java的一种数据类型,和类、接口、数组、枚举类似
- 类、方法、变量、参数、包都可以使用注解来修饰
- 注解对于它所修饰的代码并没有直接影响
1.2 用途
Annotations have a number of uses, among them:
Information for the complier - Annotations can be used by the compiler to detect errors or suppress warnings.
Compiler-time and deployment-time processing - Software tools can process annotation information to generate code, XML files, and so forth.
Runtime processing - Some annotations are available to be examined at runtime.
- 为编译器提供信息:编译器可以使用注解来检测错误或抑制警告
- 编译时和部署时处理:软件工具能处理注解信息从而生成代码,XML文件等等
- 运行时处理:有些注解可在运行时进行检查
2 使用自定义注解
注解其实就是一种标记,可以在程序代码中的关键点(类、方法、变量、参数、包)上打上这些标记,程序在编译时或运行时可以检测到这些标记从而执行一些特殊的操作
基本步骤
- 定义注解
- 配置注解 把标记打在需要用到的代码中
- 解析注解 在编译时或运行时检测到标记,并进行特殊操作
2.1 定义注解
1 //@CherryAnnotation被限定只能使用在方法上面,运行时有效
2 @Retention(RetentionPolicy.RUNTIME)
3 @Target(value = {ElementType.METHOD})
4 public @interface CherryAnnotation { //使用关键字@interface
5 public String name();
6 int age() default 18;
7 int[] array();
8 }
- 注解类型的声明:使用关键字@interface,在底层实现上,所有定义的注解都会自动继承 java.lang.annotation.Annotation接口。
- 注解类型实现部分:只能定义注解类型元素注解元素使用注意:a.访问修饰符必须为public,不写默认为public;b.该元素的类型只能是基本数据类型,String、Class、枚举类型、注解类型、数组;c.元素名称一般定义为名词,如果注解中只有一个元素,可以把名字起为valued.()仅仅只是一个特殊的语法,不能定义参数e.default为默认值,必须和定义类型一致f.如果没有默认值,代表后续使用注解时必须给改类型元素赋值
- 元注解:专门修饰注解的注解
@Target注解,是专门用来限定某个自定义注解能够被应用在哪些Java元素上面的。它使用一个枚举类型定义如下:
1 public enum ElementType {
2 /** 类,接口(包括注解类型)或枚举的声明 */
3 TYPE,
4
5 /** 属性的声明 */
6 FIELD,
7
8 /** 方法的声明 */
9 METHOD,
10
11 /** 方法形式参数声明 */
12 PARAMETER,
13
14 /** 构造方法的声明 */
15 CONSTRUCTOR,
16
17 /** 局部变量声明 */
18 LOCAL_VARIABLE,
19
20 /** 注解类型声明 */
21 ANNOTATION_TYPE,
22
23 /** 包的声明 */
24 PACKAGE
25 }
@Retention注解,用来修饰自定义注解的生命力。
注解的生命周期有三个阶段:1、Java源文件阶段;2、编译到class文件阶段;3、运行期阶段。RetentionPolicy枚举类型
1 public enum RetentionPolicy {
2 /**
3 * Annotations are to be discarded by the compiler.
4 * (注解将被编译器忽略掉)
5 * 将被限定在Java源文件中,这个注解即不会参与编译也不会在运行期起任何作用,就和注释是一样的效果,只能被阅读Java文件的人看到;
6 */
7 SOURCE,
8
9 /**
10 * Annotations are to be recorded in the class file by the compiler
11 * but need not be retained by the VM at run time. This is the default
12 * behavior.
13 * (注解将被编译器记录在class文件中,但在运行时不会被虚拟机保留,这是一个默认的行为)
14 * 将被编译到Class文件中,编译器可以在编译时根据注解做一些处理动作,但是运行时JVM(Java虚拟机)会忽略它,我们在运行期也不能读取到;
15 */
16 CLASS,
17
18 /**
19 * Annotations are to be recorded in the class file by the compiler and
20 * retained by the VM at run time, so they may be read reflectively.
21 * (注解将被编译器记录在class文件中,而且在运行时会被虚拟机保留,因此它们能通过反射被读取到)
22 * 注解可以在运行期的加载阶段被加载到Class对象中。在程序运行阶段,我们可以通过反射得到这个注解,并通过判断是否有这个注解或这个注解中属性的值,
23 * 从而执行不同的程序代码段。我们实际开发中的自定义注解几乎都是使用的RetentionPolicy.RUNTIME;
24 * @see java.lang.reflect.AnnotatedElement
25 */
26 RUNTIME
27 }
@Documented注解,是被用来指定自定义注解是否能随着被定义的java文件生成到JavaDoc文档当中。
@Inherited注解,是指定某个自定义注解如果写在了父类的声明部分,那么子类的声明部分也能自动拥有该注解。@Inherited注解只对那些@Target被定义为ElementType.TYPE的自定义注解起作用。
2.2 配置注解 把标记打在需要用到的代码中
1 public class Student {
2 @CherryAnnotation(name = "cherry-peng",age = 23,score = {99,66,77})
3 public void study(int times){
4 for(int i = 0; i < times; i++){
5 System.out.println("Good Good Study, Day Day Up!");
6 }
7 }
8 }
在定义的注解中,有些注解类型元素没有默认值,使用的时候需要在标记名后面加上(),并以“元素名1=元素值1,元素名2=元素值=2”的形式为元素赋值。(原有默认的会被覆盖)
2.3 解析注解 在编译时或运行时检测到标记,并进行特殊操作
只有当注解的保持力处于运行阶段,即使用@Retention(RetentionPolicy.RUNTIME)修饰注解时,才能在JVM运行时,检测到注解,并进行一系列特殊操作。
反射操作获取注解
public class TestAnnotation {
public static void main(String[] args){
try {
//获取Student的Class对象
Class stuClass = Class.forName("pojos.Student");
//说明一下,这里形参不能写成Integer.class,应写为int.class
Method stuMethod = stuClass.getMethod("study",int.class);
//判断该元素上是否配置有某个指定的注解
if(stuMethod.isAnnotationPresent(CherryAnnotation.class)){
System.out.println("Student类上配置了CherryAnnotation注解!");
//获取该元素上指定类型的注解
CherryAnnotation cherryAnnotation = stuMethod.getAnnotation(CherryAnnotation.class);
System.out.println("name: " + cherryAnnotation.name() + ", age: " + cherryAnnotation.age()
+ ", score: " + cherryAnnotation.score()[0]);
}else{
System.out.println("Student类上没有配置CherryAnnotation注解!");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
//getAnnotations(),该方法可以获得该对象身上配置的所有的注解,返回Annotation类型数组
特殊语法
1.如果注解本身没有注解类型元素,在使用注解的时候可以省略(),直接写为:@注解名,它和标准语法@注解名()等效
1 @Retention(RetentionPolicy.RUNTIME)
2 @Target(value = {ElementType.TYPE})
3 @Documented
4 public @interface FirstAnnotation {
5 }
6 ------------------------------------
7 //等效于@FirstAnnotation()
8 @FirstAnnotation
9 public class JavaBean{
10 //省略实现部分
11 }
2.如果注解本本身只有一个注解类型元素,而且命名为value,在使用注解的时候可以直接使用:@注解名(注解值),其等效于:@注解名(value = 注解值)
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
@Documented
public @interface SecondAnnotation {
String value();
}
------------------------------------
//等效于@ SecondAnnotation(value = "this is second annotation")
@SecondAnnotation("this is annotation")
public class JavaBean{
//省略实现部分
}
3.如果某个注解类型元素是一个数组类型,在使用时又只需要填入一个值的情况,在使用注解时可以直接写为:@注解名(类型名 = 类型值),它和标准写法:@注解名(类型名 = {类型值})等效
@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
@Documented
public @interface ThirdAnnotation {
String[] name();
}
--------------------------------------
//等效于@ ThirdAnnotation(name = {"this is third annotation"})
@ ThirdAnnotation(name = "this is third annotation")
public class JavaBean{
//省略实现部分
}
4.如果一个注解的@Target是定义为Element.PACKAGE,这个注解是配置在package-info.java中的,而不能直接在某个类的package代码上面配置。
Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。
作用在代码的注解是
1 @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
2 @Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
3 @SuppressWarnings - 指示编译器去忽略注解中声明的警告。
4 作用在其他注解的注解(或者说 元注解)是:
5 @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
6 @Documented - 标记这些注解是否包含在用户文档中。
7 @Target - 标记这个注解应该是哪种 Java 成员。
8 @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
9 从 Java 7 开始,额外添加了 3 个注解:
10 @SafeVarargs - Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
11 @FunctionalInterface - Java 8 开始支持,标识一个匿名函数或函数式接口。
12 @Repeatable - Java 8 开始支持,标识某注解可以在同一个声明上使用多次。