- 注解(annotation)概述
- 注解可以标识代码,或是为代码提供元数据
- 有成员变量的注解称为元数据
- 没有成员变量的注解称为标记
- 注解是一个附属品,依赖于其他元素(包、类、方法、属性等等)存在。
- 注解本身没有任何作用,只有被外部程序/JVM解析时才会有作用
- Java语言中的类、方法、变量、参数、包都可以被注解。
- 注解可以标识代码,或是为代码提供元数据
1 注解架构
- 架构图的左侧表示Annotation接口的组成,架构图的右侧则是继承自Annotation接口的常用注解
- 注解的本质是Annotation接口的子接口,所有注解都继承自Annotation接口
- Annotation接口与1个RetentionPolicy关联,并且与1~n个ElementType关联
2 Annotation接口
java.lang.annotation.Annotation定义
package java.lang.annotation;
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
java.lang.annotation.ElementType定义
package java.lang.annotation;
public enum ElementType {
TYPE, //类、接口(包括注释类型)或枚举声明
FIELD, //字段声明(包括枚举常量
METHOD, //方法声明
PARAMETER, //参数声明
CONSTRUCTOR, //构造方法声明
LOCAL_VARIABLE, //局部变量声明
ANNOTATION_TYPE, //注释类型声明
PACKAGE //包声明
}
java.lang.annotation.RetentionPolicy定义
package java.lang.annotation;
public enum RetentionPolicy {
SOURCE, //Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了
CLASS, //编译器将Annotation存储于类对应的.class文件中。默认行为
RUNTIME //编译器将Annotation存储于class文件中,并且可由JVM读入
}
Annotation与ElementType、RetentionPolicy
每1个Annotation对象,都会有唯一的RetentionPolicy属性以及1~n个ElementType属性。
ElementType
- ElementType是Enum枚举类型,它用来指定Annotation可以修饰的类型,
当Annotation与某个ElementType关联时,就意味着Annotation有了某种用途。
- 例如,若一个Annotation对象是METHOD类型,则该Annotation只能做作用于方法。
RetentionPolicy
- RetentionPolicy是Enum枚举类型,它用来指定Annotation保留、存在的阶段,即保留策略。
- 若Annotation的保留策略SOURCE,则意味着Annotation仅存在于编译器处理期间,编译器处理完之后,该Annotation就没用了。
- 例如
@Override
注解就是一个SOURCE保留策略的注解。当它修饰一个方法的时候,就意味着该方法覆盖父类的方法,并且在编译期间会进行语法检查。编译器处理完后,@Override
就不存在了。
- 例如
- 若Annotation的保留策略为CLASS,则意味着编译器将Annotation存储于类对应的.class文件中,但运行时无法获得
CLASS是Annotation默认的保留策略
- 若Annotation的保留策略为RUNTIME,则意味着编译器将Annotation存储于对应的.class文件中,并且在运行时可以通过反射获取到。
3 具体的注解
1 Java内置注解
- Java内置有7个注解,其中3个在java.lang包中,4个在java.lang.annotation包中。
7个注解可以分为两类
- 作用在代码的注解
- @Override
- @Deprecated
- @SuppressWarnings
- @SafeVarargs
- @FunctionalInterface
- 作用在注解的注解 / 元注解
元注解是注解的注解,作用在其他注解的定义中
- @Retention
- @Documented
- @Target
- @Repeatable
- @Inherited
- 作用在代码的注解含义表
| 注解 | 含义 |
| —- | —- |
| @Override | 修饰目标:方法
功能:标识该方法是重写方法。如果发现包含该方法的类的父类或实现的接口中并没有该方法时,会报编译错误。 | | @Deprecated | 修饰目标:类、方法、参数、构造器、实例字段等
功能:标记被修饰的目标为过时的。如果使用会报编译警告 | | @SuppressWarnngs | 修饰目标:类、方法、参数、构造器、实例字段等
功能:指示编译器忽略注解中声明的警告 | | @FunctionalInterface | 修饰目标:接口
功能:JDK8开始支持,标识接口为函数式接口 |
- 元注解含义表
所有元注解的ElementType为ANNOTATION_TYPE
注解 | 含义 |
---|---|
@Documented | 标识其修饰的注解包含到Javadoc中。 定义注解时,@Documented可有可无;若没有定义,则注解不会出现在Javadoc。 |
@Target | 用来指定注解的类型属性,即ElementType 定义注解时,@Target可有可无。若有@Target,则该注解只能用于它所指定的地方;若没有@Target,则该注解可以用于任何地方。 |
@Retention | 用来指定注解的保留策略,即RetentionPolicy |
@Repeatable | 标识注解可以多次使用,每次使用的功能不同 需要一个参数,表示用来保存被修饰的注解的容器 |
2 定义注解
- 自定义注解实例 ```java @Documented @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation{
}
- 上面的作用是定义一个注解,它的名字是MyAnnotation。定义了MyAnnotation之后,我们可以在代码中通过@MyAnnotation来使用它。
- **@interface用于声明一个注解**,同时使自定义的注解自动继承子Annotation接口。
- @Documented、@Target、@Retention都是来修饰MyAnnotation的
- **JDK注解实例**
```java
package java.lang;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
4 注解的属性
- 注解中没有方法,只有成员变量
1 定义注解的属性
注解的属性只能是以下类型
- 基本数据类型
- String
- Enum
- Annotation
- Class
- 以上类型的一维数组
注解声明属性的语法格式如下
类型 变量名() [default 默认值];
- 变量名后需要加小括号
- 可以在小括号后为变量指定默认值,如不指定,则使用注解时必须为其属性赋值
- 实例
package org.springframework.beans.factory.annotation;
import ...
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
package java.lang.annotation;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
ElementType[] value();
}
2 使用带有属性的注解
- 使用规则
- 如果定义的注解含有属性,那在使用注解时必须在注解后加上小括号并指定属性值
指定属性值的形式为**属性名1=属性值1, 属性名2=属性值2, ...**
也可以不指定属性名,但是传入的属性值的顺序必须和注解中定义属性的顺序一致
- 如果注解只有一个属性,直接写属性值即可,因为属性值与属性名的对照顺序一定不会错
- 注解属性定义中指定了默认值的参数可以不指定值,但没有的一定要指定值
- 实例
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
3 获取注解的属性
- 获取规则
- 如果想要获取注解的属性,那么注解的保留策略必须指定为RetentionPolicy.RUNTIME
- 需要通过反射机制获取反射的属性,主要涉及以下三个Class类实例方法 ```java /*判断Class对象是否存在Annotation对象/ public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) { return GenericDeclaration.super.isAnnotationPresent(annotationClass); }
/*获取Annotation对象/ public A getAnnotation(Class annotationClass) { Objects.requireNonNull(annotationClass);
return (A) annotationData().annotations.get(annotationClass);
}
/*获取所有Annotation对象数组/
public Annotation[] getAnnotations() {
return AnnotationParser.toArray(annotationData().annotations);
}
- **获取注解属性实例**
```java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MyTestAnnotation {
String name() default "cui";
int age() default 18;
}
@MyTestAnnotation(name = "father", age = 50)
class Father {
}
public class MyClass {
public static void main(String[] args) {
Class<Father> fatherClass = Father.class;
boolean annotationPresent = fatherClass.isAnnotationPresent(MyTestAnnotation.class);
if (annotationPresent) {
MyTestAnnotation annotation = fatherClass.getAnnotation(MyTestAnnotation.class);
System.out.println(annotation.name());
System.out.println(annotation.age());
}
}
}