Java 注解浅析

一、什么是注解

注解(Annotation)即代码里的特殊标记,JDK5.0 之后引入了 Annotation 的概念来描述元数据,在 Java 中元数据以标签的形式存在于 Java 代码中,但是元数据标签的存在并不影响程序代码的编译和执行

在 JavaSE 中,注解的使用目的比较简单,例如标记过时的功能(@Deprecated)、限定必须重写父类的方法(@Override)、抑制编译器警告(@SuppressWarning)等.但是在JavaEE 中注解占据了更加重要的角色,例如之前需要实现 AOP 时,需要繁冗的 XML 配置,有了注解之后实现 AOP 变得更加的简洁.注解是一种趋势,在一定程度上可以说 框架 = 注解 + 反射 + 设计模式  

  

二、常见的注解示例

1、文档注释相关注解

@author: 说明开发该模块的作者,多个作者可以用逗号分割
@version: 说明该模块的版本
@since: 说明是从哪个版本开始增加的
@param: 对方法中的参数进行说明,如果没有参数就不能使用该注解
@return: 对方法返回值的说明,如果方法的返回值类型是 void ,则不能使用该注解
@thorws: 对方法可能抛出的异常进行说明,如果方法上没有显示的用 throws 抛出异常,则不能使用该注解
 
2、JDK 内置的三个基本注解
@Override: 限定必须重写父类方法
@Deprecated: 表示修饰的元素(类、方法等)已过时,通常是因为所修饰的结构存在危险或者已经有更好的选择方式
@SuppressWarning: 抑制警告
 
3、元注解
元注解可以理解为一种基本注解,它就是可以标注在其它的注解上面的注解,元注解有五种,分别是 @Retention、@Documented、@Target、@Inherited、@Repeatable
3.1、@Rentention
Rentention 的英文为保留期的意思,当 @Rentention 应用到一个注解上的时候,它解释说明了这个注解的存活时间
1
2
3
4
5
6
7
8
9
10
11
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    /**
     * Returns the retention policy.
     * @return the retention policy
     */
  // @Retention 的取值
    RetentionPolicy value();
}

@Rentention 的取值选项如下

@Rentention(RententionPolicy.SOURCE / RententionPolicy.CLASS / RententionPolicy.RUNTIME) 

RententionPolicy.SOURCE: 注解只在源码阶段保留,编译器编译的时候将会被丢弃

RententionPolicy.CLASS: 注解只被保留至编译器编译后的字节码文件,它不会被虚拟机加载,如果没有注明使用其它的值,默认的取值就是 RententionPolicy.CLASS

RententionPolicy.RUNTIME: 注解能够保留至运行期,通过反射可以获取该注解的信息

3.2、@Target

Target 的英文意思是目标的意思,如果一个注解上面标注了 @Target 注解,那么就指定了该注解可以作用的位置(例如:类上、构造方法上、成员变量上、局部变量上等)
1
2
3
4
5
6
7
8
9
10
11
12
@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
     */  // @Target 的取值
    ElementType[] value();
}
@Target 的取值如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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
}

3.3、@Documented

如果某一个注解上面使用了 @Documented 注解,那么该注解将会被 JavaDoc 工具提取成文档,定义了 @Documented 的注解必须包含@Rentention(RetentionPolicy.RUNTIME)

3.4、@Inherited

某个注解如果使用了 @Inherited 元注解,那么该注解将具有继承性
举个例子:@TestInheritedAnnotation 被 @Inherited 修饰,父类 Animal 使用了 @TestInheritedAnnotation 注解,那么 Animal 的子类都自动具有@TestInheritedAnnotation 这个注解
 
@Inherited样例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 自定义 @TestInheritedAnnotation 注解,然后在该注解上使用元注解 @Inherited
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TestInheritedAnnotation {
    String value();
}
 
 
// 定义动物类作为父类,并且使用了 @TestInheritedAnnotation 注解
@TestInheritedAnnotation(value = "xiaomaomao")
public class Animal {
}
 
 
// 定义 Animal 的子类 Cow,测试 Cow 中是不是也有 @TestInheritedAnnotation 注解
public class Cow extends Animal {
    public static void main(String[] args) {
        System.out.println(isHasInheritedAnnotation());
    }
 
    public static String isHasInheritedAnnotation() {
        String value = "";
        // 判断 Cow 类上是否使用了 @TestInheritedAnnotation 注解
        boolean hasAnnotation = Cow.class.isAnnotationPresent(TestInheritedAnnotation.class);
        if (hasAnnotation) {
            TestInheritedAnnotation annotation = Cow.class.getAnnotation(TestInheritedAnnotation.class);
            value = annotation.value();
        }
        return value;
    }
}

3.5、@Repeatable

可重复注解, @Repeatable 注解是 JDK 8 之后才加入进来的,是 JDK 8 新特性,它的意思是注解的值可以取多个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 自定义容器注解 Person
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Person {
    // @Person 注解是 @RepeatableAnnotation 的容器,定义类型为注解数组
    RepeatableAnnotation[] value();
}
 
// 自定义注解 RepeatableAnnotation
// 使用 @Repeatable 注解的时候,必须要为当前注解定义一个容器注解,我们这里是 @Person
@Repeatable(Person.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RepeatableAnnotation {
    String role() default "coder";
}
 
// 测试类
@RepeatableAnnotation(role = "president")
@RepeatableAnnotation(role = "product manager")
@RepeatableAnnotation(role = "technology manager")
@RepeatableAnnotation(role = "cleaner")
public class RepeatableAnnotationDemo {
    public static void main(String[] args) {
        StringBuffer stringBuffer = new StringBuffer();
        // 判断 RepeatableAnnotationDemo 类上是否使用了@Person 注解
        boolean hasPersonAnnotation = RepeatableAnnotationDemo.class.isAnnotationPresent(Person.class);
        if (hasPersonAnnotation) {
            // 获取 RepeatableAnnotationDemo 类上 Person 注解
            Person personAnnotation = RepeatableAnnotationDemo.class.getAnnotation(Person.class);
            // 通过 Person 注解容器获取到该容器内的所有 @RepeatableAnnotation 注解
            RepeatableAnnotation[] repeatableAnnotations = personAnnotation.value();
            // 获取所有的 @RepeatableAnnotation 中的 role 值并拼接输出
            for (RepeatableAnnotation repeatableAnnotation : repeatableAnnotations) {
                stringBuffer.append(repeatableAnnotation.role() + "===>");
            }
            System.out.println(stringBuffer);
        }
    }
}
 
// 测试结果
president===>product manager===>technology manager===>cleaner===>

  

三、注解的属性

注解的属性也叫做成员变量,注解只有成员变量,没有方法,注解的成员变量是以 无形式参数的方法 来声明的,其方法中定义了该成员变量的类型和成员方法的名字

注意: 在注解中定义属性,属性的类型必须是八种基本数据类型、类、接口、注解以及它们的数组

如下自定义的注解中有 int 类型的 id,它有一个默认值 10086、String 类型的 userName、以及 String 数组类型的 hobby

1
2
3
4
5
6
7
@Retention(value=RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface TestAnnotation {  // 注解中可以有默认值,使用 default 关键字来指定
    int id() default 10086;
    String userName();
    String[] hobby();
}

1、如何给注解赋值

1
2
3
4
5
// 根据 @TestAnnotation 的属性类型和属性名称依次给注解赋值,多个值用逗号隔开,由于 id 属性有默认值,
// 那么这里可以不用给id赋值,如果显示的给id赋值的话,那么它会覆盖默认值
@TestAnnotation(userName = "xiaomaomao",hobby={"film","novel","play computer games"})
public class AnnotationDemo {
}

2、如果注解只有一个属性的情况

1
2
3
4
5
6
@Retention(value=RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface TestAnnotation {
  // 如果注解只有一个属性,请使用 value 来命名该属性
   String value();
}  

取值的时候需要注意

1
2
3
4
// 如果注解只有一个属性可以不写 value,直接 @TestAnnotation("xiaomaomao") 赋值也是一样的
@TestAnnotation(value="xiaomaomao")
public class AnnotationDemo {
}

  

四、自定义注解

1、定义新的 Annotation 类型使用 @interface 关键字.

2、自定义注解自动继承了java.lang.annotation.Annotation 接口.

3、Annotation 的成员变量在 Annotation 定义中以无参数方法的形式来声明,其方法名和返回值定义了该成员的名字和类型,我们称为配置参数.类型只能是八种基本数据类型、String 类型、Class 类型、enum 类型、Annotation 类型、以上所有类型的数组.

4、可以在定义 Annotation 的成员变量时为其指定初始值,指定成员变量的初始值可使用 default 关键字.

5、如果只有一个参数成员,建议使用参数名为 value.

6、如果定义的注解含有配置参数,那么使用时必须指定参数值,除非它有默认值.格式是 "参数名 = 参数值",如果只有一个参数成员,且名称为 value,可以省略 "value="

7、没有成员定义的 Annotation 称为标记;包含成员变量的 Annotation 称为元数据 Annotation.

特别注意:自定义注解必须配上注解的信息处理流程才有意义.

 
自定义注解样例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 自定义注解 TestAnnotation
@Retention(value=RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface TestAnnotation {
    int id() default 10086;
    String userName();
    String[] hobby();
}
 
// 测试类
@TestAnnotation(id=10001,userName = "xiaomaomao",hobby={"film","novel","play computer games"})
public class AnnotationDemo {
    public static void main(String[] args) {
        // 判断 AnnotationDemo 类上是否使用了 @TestAnnotation 注解
        boolean hasAnnotation = AnnotationDemo.class.isAnnotationPresent(TestAnnotation.class);
        StringBuffer stringBuffer = new StringBuffer();
        if (hasAnnotation) {
            // 获取 AnnotationDemo 上的所有注解,这里只有一个 @TestAnnotation 注解
            Annotation[] annotations = AnnotationDemo.class.getAnnotations();
            for (Annotation annotation : annotations) {
                // 获取 @TestAnnotation 注解中的属性并进行拼接
                if (TestAnnotation.class.equals(annotation.annotationType())) {
                    TestAnnotation testAnnotation = (TestAnnotation) annotation;
                    int id = testAnnotation.id();
                    String userName = testAnnotation.userName();
                    String[] hobbys = testAnnotation.hobby();
                    stringBuffer.append(id);
                    stringBuffer.append("\r\n");
                    stringBuffer.append(userName);
                    stringBuffer.append("\r\n");
                    stringBuffer.append("[");
                    for (String hobby : hobbys) {
                        stringBuffer.append(hobby+"、");
                    }
                    stringBuffer.append("]");
                    stringBuffer.append("\r\n");
                    // 获取 @TestAnnotation 上的注解类型并进行拼接
                    Annotation[] underTestAnnotations = testAnnotation.annotationType().getAnnotations();
                    for (Annotation underTestAnnotation : underTestAnnotations) {
                        Class<? extends Annotation> aClass = underTestAnnotation.annotationType();
                        stringBuffer.append(aClass);
                        stringBuffer.append("\r\n");
                    }
 
                }
                System.out.println(stringBuffer);
            }
        }
    }
}
 
// 测试结果
10001
xiaomaomao
[film、novel、play computer games、]
interface java.lang.annotation.Retention
interface java.lang.annotation.Target

  

 

 

 

posted @   变体精灵  阅读(184)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示