⾃定义注解详细介绍
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代码中的元数据,类、⽅法、变量、参数和包都可以⽤注解来修饰。注解对于它所修饰的代码并没有直接的影响。
通过官⽅描述得出以下结论:
1. 注解是⼀种元数据形式。即注解是属于java的⼀种数据类型,和类、接⼝、数组、枚举类似。
2. 注解⽤来修饰,类、⽅法、变量、参数、包。
3. 注解不会对所修饰的代码产⽣直接的影响。
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 基本语法
注解类型的声明部分:
注解在Java中,与类、接⼝、枚举类似,因此其声明语法基本⼀致,只是所使⽤的关键字有所不同@interface。在底层实现上,所有定义的注解都会⾃动继承java.lang.annotation.Annotation接⼝。
public @interface CherryAnnotation {
}
注解类型的实现部分:
根据我们在⾃定义类的经验,在类的实现部分⽆⾮就是书写构造、属性或⽅法。但是,在⾃定义注解中,其实现部分只能定义⼀个东西:注解类型元素(annotation type element)。咱们来看看其语法:
public @interface CherryAnnotation {
public String name();
int age();
int[]array();
}
也许你会认为这不就是接⼝中定义抽象⽅法的语法嘛?别着急,咱们看看下⾯这个:
public @interface CherryAnnotation {
public String name();
int age()default18;
int[]array();
}
看到关键字default了吗?还觉得是抽象⽅法吗?
注解⾥⾯定义的是:注解类型元素!
定义注解类型元素时需要注意如下⼏点:
1. 访问修饰符必须为public,不写默认为public;
2. 该元素的类型只能是基本数据类型、String、Class、枚举类型、注解类型(体现了注解的嵌套效果)以及上述类型的⼀位数组;
java类的概念3. 该元素的名称⼀般定义为名词,如果注解中只有⼀个元素,请把名字起为value(后⾯使⽤会带来便利操作);
4. ()不是定义⽅法参数的地⽅,也不能在括号中定义任何参数,仅仅只是⼀个特殊的语法;
5. default代表默认值,值必须和第2点定义的类型⼀致;
6. 如果没有默认值,代表后续使⽤注解时必须给该类型元素赋值。
可以看出,注解类型元素的语法⾮常奇怪,即⼜有属性的特征(可以赋值),⼜有⽅法的特征(打上了⼀对括号)。但是这么设计是有道理的,我们在后⾯的章节中可以看到:注解在定义好了以后,使⽤的时候操作元素类型像在操作属性,解析的时候操作元素类型像在操作⽅法。
2.2 常⽤的元注解
⼀个最最基本的注解定义就只包括了上⾯的两部分内容:1、注解的名字;2、注解包含的类型元素。但是,我们在使⽤JDK⾃带注解的时候发现,有些注解只能写在⽅法上⾯(⽐如@Override);有些却可以写在类的上⾯(⽐如@Deprecated)。当然除此以外还有很多细节性的定义,那么这些定义该如何做呢?接下来就该元注解出场了!
元注解:专门修饰注解的注解。它们都是为了更好的设计⾃定义注解的细节⽽专门设计的。我们为⼤家⼀个个来做介绍。
2.2.1 @Target
@Target注解,是专门⽤来限定某个⾃定义注解能够被应⽤在哪些Java元素上⾯的。它使⽤⼀个枚举类型定义如下:
public enum ElementType {
/** 类,接⼝(包括注解类型)或枚举的声明 */
TYPE,
/** 属性的声明 */
FIELD,
/** ⽅法的声明 */
METHOD,
/** ⽅法形式参数声明 */
PARAMETER,
/** 构造⽅法的声明 */
CONSTRUCTOR,
/** 局部变量声明 */
LOCAL_VARIABLE,
/** 注解类型声明 */
ANNOTATION_TYPE,
/
** 包的声明 */
PACKAGE
}
//@CherryAnnotation被限定只能使⽤在类、接⼝或⽅法上⾯
@Target(value ={ElementType.TYPE,ElementType.METHOD})
public @interface CherryAnnotation {
String name();
int age()default18;
int[]array();
}
2.2.2 @Retention
@Retention注解,翻译为持久⼒、保持⼒。即⽤来修饰⾃定义注解的⽣命⼒。
注解的⽣命周期有三个阶段:1、Java源⽂件阶段;2、编译到class⽂件阶段;3、运⾏期阶段。同样使⽤了RetentionPolicy枚举类型定义了三个阶段:
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
* (注解将被编译器忽略掉)
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time.  This is the default
* behavior.
* (注解将被编译器记录在class⽂件中,但在运⾏时不会被虚拟机保留,这是⼀个默认的⾏为)
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
* (注解将被编译器记录在class⽂件中,⽽且在运⾏时会被虚拟机保留,因此它们能通过反射被读取到)
* @see flect.AnnotatedElement
*/
RUNTIME
}
我们再详解⼀下:
1. 如果⼀个注解被定义为RetentionPolicy.SOURCE,则它将被限定在Java源⽂件中,那么这个注解即不会参与编译也不会在运⾏期起
任何作⽤,这个注解就和⼀个注释是⼀样的效果,只能被阅读Java⽂件的⼈看到;
2. 如果⼀个注解被定义为RetentionPolicy.CLASS,则它将被编译到Class⽂件中,那么编译器可以在编译时根据注解做⼀些处理动
作,但是运⾏时JVM(Java虚拟机)会忽略它,我们在运⾏期也不能读取到;
3. 如果⼀个注解被定义为RetentionPolicy.RUNTIME,那么这个注解可以在运⾏期的加载阶段被加载到Class对象中。那么在程序运⾏
阶段,我们可以通过反射得到这个注解,并通过判断是否有这个注解或这个注解中属性的值,从⽽执⾏不同的程序代码段。我们实际开发中的⾃定义注解⼏乎都是使⽤的RetentionPolicy.RUNTIME;
4. 在默认的情况下,⾃定义注解是使⽤的RetentionPolicy.CLASS。
2.2.3 @Documented
@Documented注解,是被⽤来指定⾃定义注解是否能随着被定义的java⽂件⽣成到JavaDoc⽂档当中。
2.2.4 @Inherited
@Inherited注解,是指定某个⾃定义注解如果写在了⽗类的声明部分,那么⼦类的声明部分也能⾃动拥有该注解。@Inherited注解只对那些@Target被定义为ElementType.TYPE的⾃定义注解起作⽤。
3 ⾃定义注解的配置使⽤
回顾⼀下注解的使⽤流程:
第⼀步,定义注解——相当于定义标记;
第⼆步,配置注解——把标记打在需要⽤到的程序代码中;
第三步,解析注解——在编译期或运⾏时检测到标记,并进⾏特殊操作。
到⽬前为⽌我们只是完成了第⼀步,接下来我们就来学习第⼆步,配置注解,如何在另⼀个类当中配
置它。
3.1 在具体的Java类上使⽤注解
⾸先,定义⼀个注解、和⼀个供注解修饰的简单Java类
@Retention(RetentionPolicy.RUNTIME)
@Target(value ={ElementType.METHOD})
@Documented
public @interface CherryAnnotation {
String name();
int age()default18;
int[]score();
}
public class Student{
public void study(int times){
for(int i =0; i < times; i++){
System.out.println("Good Good Study, Day Day Up!");
}
}
}
简单分析下:
1. CherryAnnotation的@Target定义为ElementType.METHOD,那么它书写的位置应该在⽅法定义的上⽅,即:public void
study(int times)之上;
2. 由于我们在CherryAnnotation中定义的有注解类型元素,⽽且有些元素是没有默认值的,这要求我们在使⽤的时候必须在标记名后
⾯打上(),并且在()内以“元素名=元素值“的形式挨个填上所有没有默认值的注解类型元素(有默认值的也可以填上重新赋值),中间⽤“,”号分割;
所以最终书写形式如下:
public class Student {
@CherryAnnotation(name ="cherry-peng",age =23,score ={99,66,77})
public void study(int times){
for(int i =0; i < times; i++){
System.out.println("Good Good Study, Day Day Up!");
}
}
}
3.2 特殊语法
特殊语法⼀:
如果注解本⾝没有注解类型元素,那么在使⽤注解的时候可以省略(),直接写为:@注解名,它和标准语法@注解名()等效!
@Retention(RetentionPolicy.RUNTIME)
@Target(value ={ElementType.TYPE})
@Documented
public @interface FirstAnnotation {
}
//等效于@FirstAnnotation()
@FirstAnnotation
public class JavaBean{
//省略实现部分
}
特殊语法⼆:
如果注解本本⾝只有⼀个注解类型元素,⽽且命名为value,那么在使⽤注解的时候可以直接使⽤:@注解名(注解值),其等效于:@注解名(value = 注解值)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。