注重版权,若要转载烦请附上作者和链接
作者:Joshua_yi
链接:https://blog.csdn.net/weixin_44984664/article/details/122088060
文章目录
一、注解
注解Annotation,也叫元数据
(一)注解的作用
我们可以从注解作用的对象上分析注解的作用
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
- TYPE:注解可以作用在类、接口(包括其他注解),枚举类型上
- ANNOTATION_TYPE:其他注解上
- FIELD:可以作用在类的属性上
- METHOD:方法上(不包括构造方法)
- CONSTRUCTOR:构造方法上
- PARAMETER:方法参数上
- LOCAL_VARIABLE:局部变量上
- PACKAGE:作用在包上,具体用法可以参考https://blog.csdn.net/weixin_38321868/article/details/118942209
- TYPE_PARAMETER :表示该注解能写在类型参数的声明语句中。(eg:泛型声明)
- TYPE_USE:表示注解可以任何用到类型的地方使用,无处不在的注解,可以让编译器执行更严格的代码检查,从而提高程序的健壮性。比如可以应用在以下地方
- 创建对象(用 new 关键字创建)
- 类型转换
- 使用 implements 实现接口
- 使用 throws 声明抛出异常
public class Solution {
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_PARAMETER)
public @interface isParam {
}
public <@isParam T> void fun1(T ob) {
System.out.println(ob);
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface isNotNull {
}
public <@isNotNull T> void fun2(@isNotNull T ob) throws @isNotNull Exception {
String s = (@isNotNull String) "11111";
System.out.println(ob);
}
}
注解可以对这些程序元素添加一些额外的信息,这些信息可以用于之后的一些操作。
根据一个注解是否包含成员函数,可以将注解大致分为两类
标记注解:没有成员变量的Annotation被称为标记。这种Annotation仅用自身的存在与否来为我们提供信息,例如@override等。
元数据注解:包含成员变量的Annotation。因为它们可以接受更多的元数据,因此被称为元数据Annotation。 成员以无参数的方法的形式被声明,其方法名和返回值定义了该成员变量的名字和类型。
PS:注解没有成员变量,只有方法Annotation的成员变量在Annotation定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
可以使用default
关键字为成员变量指定默认值
(二)源码解读
注解相关源码在java.lang.annotation
包下
- Annotation 是所有注解继承的接口,这也就是为什么说,接口底层是一个接口。
(定义了@interface, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口) - AnnotationFormatError继承自Error,当注释解析器从类文件读取注释并确定该注解有错误时抛出
- AnnotationTypeMismatchException继承自RuntimeException,表示程序试图访问注释的某个元素,该注释的类型在编译(或序列化)后发生了更改,这时候抛出该异常
- IncompleteAnnotationException继承自RuntimeException,表示程序试图访问注释类型的元素,该元素是在注释被编译(或序列化)后添加到注释类型定义中的,也就是找不到该注解中的一些成员变量。
- ElementType 指定注解在Java程序中注释可能出现的语法位置。
- RetentionPolicy 描述了保留注解的策略,源码级、字节码、运行时
- @Native 修饰类属性,表示这个变量可以被本地代码引用,常常被代码生成工具使用。
- 其他五个注解都可以修饰其他注解,被称为元注解。(Meta-Annotation)
PS:看了各种官方文档,还没有找到对这个Meta-Annotation的准确定义。
只知道可以作用在其他注解上的注解叫做元注解。为了区别于自己定义的元注解,以下统称为基本元注解
二、基本元注解与注解解析
在说明该五个基本元注解之前,需要对ElementType
(前文已经有相关阐述)和RetentionPolicy
进行讲解
(一)ElementType
见前文
(二)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,
/**
* 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.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
-
RetentionPolicy.SOURCE:表明注解会被编译器丢弃,字节码中不会带有注解信息。一般用于需要在编译器层面就可以发现的问题。比如@override就是代码级别可见,与编译器相结合来检查问题。
-
RetentionPolicy.CLASS:表明注解会被写入字节码文件,是@Retention的默认值。简单来说,其自定义注解解析需要实现AbstractProcessor的process()方法。基本使用可以参考该文章 java注解之编译时注解RetentionPolicy.CLASS 基本用法
-
RetentionPolicy.RUNTIME:表明注解会被写入字节码文件,并且能够被JVM 在运行时获取通过反射的方式解析到。
反射常用到的函数如下
- T getAnnotation(Class) : 获得当前对象的指定的注解。
- Annotation[] getAnnotations() X: 获得当前对象的所有注解
- boolean isAnnotationPresent(annotationClass): 当前对象是否有注解。
PS:定义了一个注解之后,只是一个空的定义,需要通过定义注解解析来赋予其实在的意义。常用的自定义的注解解析,是通过反射来进行相关操作的,也就是说需要指定注解的Retention到RUNTIME。
(三)@Documented
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Documented {
}
在用javadoc命令生成API文档后,文档里会出现该注解说明
(四)@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
@Inherited 表示该注解具有继承性。
即如果一个使用了@Inherited 修饰的annotation 类型被用于一个class,则这个annotation 将被用于该class 的子类。
(五)@Retention
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
指定注解生命周期RetentionPolicy
上面已经详细进行了解释
(六)@Target
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation type
* can be applied to.
* @return an array of the kinds of elements an annotation type
* can be applied to
*/
ElementType[] value();
}
指定注解作用对象ElementType
上面已经详细进行了解释
(七)@Repeatable
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
/**
* Indicates the <em>containing annotation type</em> for the
* repeatable annotation type.
* @return the containing annotation type
*/
Class<? extends Annotation> value();
}
@Repeatable是JDK1.8新加入的,它表示在同一个位置重复相同的注解。在没有该注解前,要想在同一个类型上使用相同的注解需要指定value为数组。
eg:
@FilterPath("/update")
@FilterPath("/add")
public class A {}
三、总结
以上部分结合源码对Java注解进行了较为详细的阐述。重点在于注解、基本元注解以及相关使用场景做了解释。很多部分仅代表自己的观点,如有错误还请各位指正~~
参考文档
https://blog.csdn.net/chenliguan/article/details/78304025
https://www.jianshu.com/p/613e3b1bd842
https://blog.csdn.net/sjh66655/article/details/112901868
https://blog.csdn.net/xtho62/article/details/113816008
https://blog.csdn.net/u014207606/article/details/52291951