注解:
一、 标准注解:@Override、@Deprecated、@SuppressWarnings
二、 元注解:用于修饰注解的注解,通常用在注解的定义上
1. @Target : 注解的作用目标
- package、type(类、接口、枚举、Annotation类型)
- 类型成员(方法、构造方法、成员变量、枚举值)
- 方法参数和本地变量(如循环变量、catch参数)
查看@Target注解源码:
package java.lang.annotation;
@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();
}
其中定义了一个value()的数组,查看数组是个枚举类:
package java.lang.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
}
前面的几个都好理解,注释都有,后面在JDK1.8中新增了两个(TYPE_PARAMETER和TYPE_USE)
其中TYPE_USE包含以上所有类型,即如果想让注解标注在上面任何类型,就可以选用TYPE_USE了。而另一个TYPE_PARAMETER也是做参数标注的,和PARAMETER不同的是,TYPE_PARAMETER是作用在泛型参数上的。
例如自定义TYPE_PARAMETER类型的注解@TypeParameterAnnotation:
@Target(ElementType.TYPE_PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface TypeParameterAnnotation {
}
//将@TypeParameterAnnotation作用于泛型参数之上
public class TypeParameterDemo<@TypeParameterAnnotation T> {
public <@TypeParameterAnnotation U> T foo(T t){
return t;
}
}
2. @Retention : 标注注解被保留时间的长短
- 定义注解的生命周期
查看@Retention注解源码:
package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
/**
* Returns the retention policy.
* @return the retention policy
*/
RetentionPolicy value();
}
其中定义了一个类型为RetentionPolicy的value(),进一步查看RetentionPolicy是个枚举类:
package java.lang.annotation;
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
}
- SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
- CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
- RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
这三个可以简单的理解为Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码
3. @Documented : 注解是否应该被包含在JavaDoc文档中
4. @Inherited : 是否允许子类继承该注解
关于@Inherited介绍看这篇文章比较好 https://www.jianshu.com/p/7f54e7250be3
三、 自定义注解
1. 格式
public @interface 注解名 {
修饰符 返回值 属性名() 默认值;
修饰符 返回值 属性名() 默认值;
}
2. 注解属性支持的类型
- 所有基本类型(int float double boolean byte char short long)
- String类型
- Class类型
- Enum类型
- Annotation类型
- 以上所有类型的数组
3. 关于类的基本信息
-
Package
public class Package implements java.lang.reflect.AnnotatedElement { //省略... }
Package实现了AnnotatedElement
-
Class
public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement { //省略... }
查看Class发现其实现了AnnotatedElement
-
Constructor
public final class Constructor<T> extends Executable { //省略... } public abstract class Executable extends AccessibleObject implements Member, GenericDeclaration { //省略... } public class AccessibleObject implements AnnotatedElement { //省略... }
Constructor也是实现了AnnotatedElement,其间还继承了是否可执行以及是否可达两个类
-
Field
public final class Field extends AccessibleObject implements Member { //省略... } public class AccessibleObject implements AnnotatedElement { //省略... }
Field也实现了AnnotatedElement,其继承了是否可达这个类
-
Method
public final class Method extends Executable { //省略... } public abstract class Executable extends AccessibleObject implements Member, GenericDeclaration { //省略... } public class AccessibleObject implements AnnotatedElement { //省略... }
Method与Constructor类似,都存在是否可执行以及是否可达,而Field只存在是否可达情况(Field并不被执行),但是四个类都实现了AnnotatedElement,来查看一下其源码(注释太多都被我干掉了):
public interface AnnotatedElement {
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
Annotation[] getAnnotations();
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
/*
* Definition of associated: directly or indirectly present OR
* neither directly nor indirectly present AND the element is
* a Class, the annotation type is inheritable, and the
* annotation type is associated with the superclass of the
* element.
*/
T[] result = getDeclaredAnnotationsByType(annotationClass);
if (result.length == 0 && // Neither directly nor indirectly present
this instanceof Class && // the element is a class
AnnotationType.getInstance(annotationClass).isInherited()) { // Inheritable
Class<?> superClass = ((Class<?>) this).getSuperclass();
if (superClass != null) {
// Determine if the annotation is associated with the
// superclass
result = superClass.getAnnotationsByType(annotationClass);
}
}
return result;
}
default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
Objects.requireNonNull(annotationClass);
// Loop over all directly-present annotations looking for a matching one
for (Annotation annotation : getDeclaredAnnotations()) {
if (annotationClass.equals(annotation.annotationType())) {
// More robust to do a dynamic cast at runtime instead
// of compile-time only.
return annotationClass.cast(annotation);
}
}
return null;
}
default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {
Objects.requireNonNull(annotationClass);
return AnnotationSupport.
getDirectlyAndIndirectlyPresent(Arrays.stream(getDeclaredAnnotations()).
collect(Collectors.toMap(Annotation::annotationType,
Function.identity(),
((first,second) -> first),
LinkedHashMap::new)),
annotationClass);
}
Annotation[] getDeclaredAnnotations();
}
根据单一职责原则,接口中只定义方法,实现都在实现类中实现的,所以Package、Class、Constructor、Field、Method都是有其对应的实现方法的,这里我们不关心他们是怎么实现的,而查看这几个方法都是做什么的,这里有getDeclaredAnnotation和getAnnotation两个方法,Declared是自获取自身的注解,不包括继承过来的,而getAnnotation是或者所有注解,包括继承过来的。
//判断指定类型的注解是否存在于此元素上
default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
return getAnnotation(annotationClass) != null;
}
//获取对象上单个指定注解
<T extends Annotation> T getAnnotation(Class<T> annotationClass);
//获取对象上面所有的注解
Annotation[] getAnnotations();
//获取对象上面指定类型的注解数组
default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
T[] result = getDeclaredAnnotationsByType(annotationClass);
if (result.length == 0 && // Neither directly nor indirectly present
this instanceof Class && // the element is a class
AnnotationType.getInstance(annotationClass).isInherited()) { // Inheritable
Class<?> superClass = ((Class<?>) this).getSuperclass();
if (superClass != null) {
// Determine if the annotation is associated with the
// superclass
result = superClass.getAnnotationsByType(annotationClass);
}
}
return result;
}
前几个都没问题,后面getAnnotationsByType是JDK1.8新增的属性,我们来举例说明:
首先定义两个注解
@Repeatable(MultipleAnnotation.class)
public @interface TypeTestAnnotation {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MultipleAnnotation {
TypeTestAnnotation[] value();
}
public class ZhangSan {
@TypeTestAnnotation("第一个")
@TypeTestAnnotation("第二个")
@TypeTestAnnotation("第三个")
String language;
public static void main(String[] args) throws NoSuchFieldException {
TypeTestAnnotation[] languages = ZhangSan.class.getDeclaredField("language").getDeclaredAnnotationsByType(TypeTestAnnotation.class);
for (TypeTestAnnotation language : languages) {
System.out.println(language);
}
}
}
out:
@com.demo.annotation.TypeTestAnnotation(value=第一个)
@com.demo.annotation.TypeTestAnnotation(value=第二个)
@com.demo.annotation.TypeTestAnnotation(value=第三个)
其中@Repeatable注解也是在JDK1.8中新增的,旨在扩展重复注解,这里的TypeTestAnnotation指向存储注解MultipleAnnotation,1.8之前如果想使用重复注解得这样定义:
public @interface TypeTestAnnotation {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MultipleAnnotation {
TypeTestAnnotation[] value();
}
public class ZhangSan {
@MultipleAnnotation({@TypeTestAnnotation("第一个"),@TypeTestAnnotation("第二个"),@TypeTestAnnotation("第三个")})
String language;
public static void main(String[] args) throws NoSuchFieldException {
MultipleAnnotation language = ZhangSan.class.getDeclaredField("language").getDeclaredAnnotation(MultipleAnnotation.class);
System.out.println(language);
}
}
out:
@com.demo.annotation.MultipleAnnotation(value=[@com.demo.annotation.TypeTestAnnotation(value=第一个), @com.demo.annotation.TypeTestAnnotation(value=第二个), @com.demo.annotation.TypeTestAnnotation(value=第三个)])
这样做也是可以,但是很明显结合了@Repeatable的getDeclaredAnnotationsByType更加的人性化。
讲完AnnotatedElement之后,进入以下实战测试:
四、 自定义注解实战
1. 先定义两个注解:
//类和方法
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CourseInfoAnnotation {
//课程名称
String courseName();
//课程标签
String courseTag();
//课程简介
String courseProfile();
//课程序号
int courseIndex() default 1503;
}
//字段
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonInfoAnnotation {
//名字
String name();
//年龄
int age() default 18;
//性别
String gender() default "男";
//开发语言
String[] language();
}
2. 在定义一个用于标注的类English:
@CourseInfoAnnotation(
courseName = "类名字",
courseTag = "类标签",
courseProfile = "类简介")
public class English {
@PersonInfoAnnotation(name = "Joker", language = {"Java", "C++", "Python"})
private String author;
@CourseInfoAnnotation(
courseName = "方法名字",
courseTag = "方法标签",
courseProfile = "方法简介",
courseIndex = 200)
public void getCourseInfo(){
}
}
3. 编写测试类AnnotationParser:
public class AnnotationParser {
//解析类的注解
public static void parseTypeAnnotation() throws ClassNotFoundException {
Class<?> clazz = Class.forName("com.demo.annotation.English");
//获取class的注解,而非成员或方法的注解
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获取单个注解
CourseInfoAnnotation annotation2 = clazz.getAnnotation(CourseInfoAnnotation.class);
System.out.println(annotation2);
}
//解析成员变量的注解
public static void parseFieldAnnotation() throws ClassNotFoundException {
Class<?> clazz = Class.forName("com.demo.annotation.English");
Field[] declaredFields = clazz.getDeclaredFields();
for (Field declaredField : declaredFields) {
//是否被PersonInfoAnnotation注解修饰
boolean hasAnnotation = declaredField.isAnnotationPresent(PersonInfoAnnotation.class);
if(hasAnnotation){
PersonInfoAnnotation annotation = declaredField.getAnnotation(PersonInfoAnnotation.class);
System.out.println(annotation);
}
}
}
//解析方法的注解
public static void parseMethodAnnotation() throws ClassNotFoundException {
Class<?> clazz = Class.forName("com.demo.annotation.English");
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
boolean hasAnnotation = declaredMethod.isAnnotationPresent(CourseInfoAnnotation.class);
if(hasAnnotation){
CourseInfoAnnotation annotation = declaredMethod.getAnnotation(CourseInfoAnnotation.class);
System.out.println(annotation);
}
}
}
public static void main(String[] args) throws ClassNotFoundException {
parseTypeAnnotation();
System.out.println("-----------------------------------------------------");
parseFieldAnnotation();
System.out.println("-----------------------------------------------------");
parseMethodAnnotation();
}
}
out:
@com.demo.annotation.CourseInfoAnnotation(courseIndex=1503, courseName=类名字, courseTag=类标签, courseProfile=类简介)
@com.demo.annotation.CourseInfoAnnotation(courseIndex=1503, courseName=类名字, courseTag=类标签, courseProfile=类简介)
-----------------------------------------------------
@com.demo.annotation.PersonInfoAnnotation(gender=男, age=18, name=Joker, language=[Java, C++, Python])
-----------------------------------------------------
@com.demo.annotation.CourseInfoAnnotation(courseIndex=200, courseName=方法名字, courseTag=方法标签, courseProfile=方法简介)
结果无误,此篇结。