0%

Java 注解笔记

前言

之前我常用的语言是 PHP,受限于语言特性,说实话 PHP 被很多公司边缘化了,感觉后面发展空间和路子并不是很大,加上很多解决方案还是 Java 的比较多,且资料更加健全,我一头扎入了 Java 的学习中。

注解和反射在我们在现在的框架中常常看到,查看框架的底层也会发现反射的身影,如果我们不了解这些通用的方法,显然有点说不过去。边学边记录,记录自己的学习路线。

注解

基本概念

  • Annotation 是从 JDK5.0 开始引入的新技术
  • 作用:
    • 不是程序本身,可以对程序做出解释(这一点和注释没有什么区别)
    • 可以被其他程序(比如:编译器等)读取
  • Annotation 的格式:
    • 注解以“@注解名”在代码中存在,还可以添加一些参数值,例如:@SuppressWarnings(value=”unchecked”)
  • Annotation 在哪里使用?
    • 可以附加在 package、class、method、field 等上面,相当于给爱他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问。

内置注解

  • @Override:定义在java.lang.Override中,此注解只适用于修饰方法,表示一个方法声明打算重写超类中的一个方法声明。
  • @Deprecated:定义在java.lang.Deprecated中,此注解可用于修饰方法,属性,类,表示不鼓励大家使用这个元素,通常是因为它很危险或者有更好的选择。
  • @SuppressWarning:定义在java.lang.SuppressWarning中,用来抑制编译时的警告信息
    • 与前面两个注解有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好了的,我们选择性使用就好了。
      • @SuppressWarning(“all”)
      • @SuppressWarning(“unchecked”)
      • @SuppressWarning(value={“unchecked”, “deprecation”})
      • 等等……

元注解

基本概念

  • 元注解的作用就是负责注解其他注解,Java 定义了 4 个标准的 meta-annotation 类型,他们被用来提供对其他 annotation 类型作说明。
  • 这些类型和它们所支持的类在java.lang.annotation包中找到。(@Target,@Retention,@Documented,@Inherited)
    • @Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
    • @Retention:表示需要在什么级别保存该注解信息,用于描述注解的声明周期(SOURCE < CLASS < RUNTIME)
    • @Documented:说明该注解将包含在 JavaDoc 中
    • @Inherited:说明子类可以继承父类中的该注解

@Target 用于描述注解的作用范围

我们查看源代码可以知道,Target 可以传入一个值 value 这个 value 的类型为 ElementType

1
public @interface Target {
2
    ElementType[] value();
3
}

让我们看看这个ElementType枚举类包含的值,表示注解作用在什么上面:

  • TYPE:作用在类、接口,包括注解、枚举

    1
    // 我们先定义一个注解,文件为 MyAnnotationDemo01.java
    2
    package com.yubulang.demo01;
    3
    4
    import java.lang.annotation.ElementType;
    5
    import java.lang.annotation.Target;
    6
    7
    @Target(value = {ElementType.TYPE})
    8
    public @interface MyAnnotationDemo01 {
    9
    }

    然后我们定义这个注解可以使用的对象,我是用的 idea,如果注解不支持这个类型,注解是会有红线提示。

    1
    package com.yubulang.demo01;
    2
    3
    // 作用在类上
    4
    @MyAnnotationDemo01
    5
    public class AnnotationDemo {
    6
    7
    }
    8
    9
    // 作用在接口上
    10
    @MyAnnotationDemo01
    11
    interface TestInterface {
    12
    13
    }
    14
    15
    // 作用在注解上
    16
    @MyAnnotationDemo01
    17
    @interface TestAnnotation {
    18
    19
    }
    20
    21
    // 作用在枚举类上
    22
    @MyAnnotationDemo01
    23
    enum TestEnum {
    24
        HELLO,
    25
        WORLD,
    26
    }
  • FIELD:作用在属性字段,包括枚举和常量

    1
    // 定义一个注解
    2
    package com.yubulang.demo02;
    3
    4
    import java.lang.annotation.ElementType;
    5
    import java.lang.annotation.Target;
    6
    7
    @Target(ElementType.FIELD)
    8
    public @interface MyAnnotationDemo01 {
    9
    }

    然后我们定义这个注解可以使用的对象:

    1
    package com.yubulang.demo02;
    2
    3
    public class AnnotationDemo01 {
    4
        // 作用在name属性
    5
        @MyAnnotationDemo01
    6
        protected String name;
    7
    8
        // 作用在常量上
    9
        @MyAnnotationDemo01
    10
        protected final String id = "0001";
    11
    12
        enum HelloEnum {
    13
            THING01,
    14
            THING02
    15
        }
    16
    17
        // 作用在枚举类型
    18
        @MyAnnotationDemo01
    19
        protected HelloEnum helloEnum = HelloEnum.THING01;
    20
    }
  • METHOD:作用在方法

    1
    // 定义一个注解
    2
    package com.yubulang.demo03;
    3
    4
    import java.lang.annotation.ElementType;
    5
    import java.lang.annotation.Target;
    6
    7
    @Target(ElementType.METHOD)
    8
    public @interface MyAnnotationDemo01 {
    9
    }

    然后我们定义这个注解可以使用的对象:

    1
    package com.yubulang.demo03;
    2
    3
    public class AnnotationDemo01 {
    4
        // 作用于方法
    5
        @MyAnnotationDemo01
    6
        public void test() {
    7
    8
        }
    9
    }
  • PARAMETER:作用在方法参数上

    1
    // 定义一个注解
    2
    package com.yubulang.demo04;
    3
    4
    import java.lang.annotation.ElementType;
    5
    import java.lang.annotation.Target;
    6
    7
    @Target(ElementType.PARAMETER)
    8
    public @interface MyAnnotationDemo01 {
    9
    }

    然后我们定义这个注解可以使用的对象:

    1
    package com.yubulang.demo04;
    2
    3
    public class AnnotationDemo {
    4
        // 作用在方法的参数上
    5
        public void test(@MyAnnotationDemo01 String name) {
    6
    7
        }
    8
    }
  • CONSTRUCTOR:作用在构造方法上

    1
    // 定义一个注解
    2
    package com.yubulang.demo05;
    3
    4
    import java.lang.annotation.ElementType;
    5
    import java.lang.annotation.Target;
    6
    7
    @Target(ElementType.CONSTRUCTOR)
    8
    public @interface MyAnnotationDemo01 {
    9
    }

    然后我们定义这个注解可以使用的对象:

    1
    package com.yubulang.demo05;
    2
    3
    public class AnnotationDemo {
    4
        // 作用在构造函数上
    5
        @MyAnnotationDemo01
    6
        public AnnotationDemo() {
    7
    8
        }
    9
    10
        // 这里会报错,因为不是构造方法
    11
        // @MyAnnotationDemo01
    12
        // public void test() {
    13
        //
    14
        // }
    15
    }
  • LOCAL_VARIABLE:作用在局部变量上

    1
    // 定义一个注解
    2
    package com.yubulang.demo06;
    3
    4
    import java.lang.annotation.ElementType;
    5
    import java.lang.annotation.Target;
    6
    7
    @Target(ElementType.LOCAL_VARIABLE)
    8
    public @interface MyAnnotationDemo01 {
    9
    }

    然后我们定义这个注解可以使用的对象:

    1
    package com.yubulang.demo06;
    2
    3
    public class AnnotationDemo {
    4
        public void test() {
    5
            // 作用在局部变量上
    6
            @MyAnnotationDemo01
    7
            String name = "李雷与韩梅梅";
    8
        }
    9
    }
  • ANNOTATION_TYPE:作用在注解上

    1
    // 定义一个注解
    2
    package com.yubulang.demo07;
    3
    4
    import java.lang.annotation.ElementType;
    5
    import java.lang.annotation.Target;
    6
    7
    @Target(ElementType.ANNOTATION_TYPE)
    8
    public @interface MyAnnotationDemo {
    9
    }

    然后我们定义这个注解可以使用的对象:

    1
    package com.yubulang.demo07;
    2
    3
    // 作用在注解上
    4
    @MyAnnotationDemo
    5
    public @interface AnnotationDemo {
    6
    7
    }
  • PACKAGE:作用在包上

    1
    // 定义一个注解
    2
    package com.yubulang.demo08;
    3
    4
    import java.lang.annotation.ElementType;
    5
    import java.lang.annotation.Target;
    6
    7
    @Target(ElementType.PACKAGE)
    8
    public @interface MyAnnotationDemo {
    9
    }

    然后我们定义这个注解可以使用的对象,这里有个特殊我们需要创建一个 package-info.java 这个是包的描述文件。

    1
    // package-info.java
    2
    // 作用在包上
    3
    @MyAnnotationDemo
    4
    package com.yubulang.demo08;
  • TYPE_PARAMETER:作用在定义泛型的类型参数上

    1
    // 定义一个注解
    2
    package com.yubulang.demo09;
    3
    4
    import java.lang.annotation.ElementType;
    5
    import java.lang.annotation.Target;
    6
    7
    @Target(ElementType.TYPE_PARAMETER)
    8
    public @interface MyAnnotationDemo {
    9
    }

    然后我们定义这个注解可以使用的对象:

    1
    package com.yubulang.demo09;
    2
    3
    // 作用在定义泛型类型参数上
    4
    class Demo<@MyAnnotationDemo T> {
    5
        private T data;
    6
    }
    7
    8
    public class AnnotationDemo {
    9
        public void test() {
    10
            Demo<String> demo = new Demo<>();
    11
        }
    12
    }
  • TYPE_USE:作用在泛型类型使用参数上

    1
    // 定义一个注解
    2
    package com.yubulang.demo10;
    3
    4
    import java.lang.annotation.ElementType;
    5
    import java.lang.annotation.Target;
    6
    7
    @Target(ElementType.TYPE_USE)
    8
    public @interface MyAnnotationDemo {
    9
    }

    然后我们定义这个注解可以使用的对象:

    1
    package com.yubulang.demo10;
    2
    3
    class Demo<T> {
    4
        private T data;
    5
    }
    6
    7
    public class AnnotationDemo {
    8
        // 作用在泛型类型使用时
    9
        Demo<@MyAnnotationDemo String> demo = new Demo<>();
    10
    }

@Retention 用于表示注解的生命周期

我们查看源代码可以知道,Retention 可以传入 value 这个值的类型为RetentionPolicy 枚举类

1
public @interface Retention {
2
    /**
3
     * Returns the retention policy.
4
     * @return the retention policy
5
     */
6
    RetentionPolicy value();
7
}

我们一般写代码的时候是源代码阶段(SOURCE),编译成 CLASS 的时候就是(CLASS),运行的时候就是(RUNTIME)。作用域由大到小应该是:RUNTIME > CLASS > SOURCE。

这个理解起来想容易,我们来使用一下:

1
// 例子一=============================
2
package com.yubulang.demo11;
3
4
import java.lang.annotation.Retention;
5
import java.lang.annotation.RetentionPolicy;
6
7
// 只有在源代码时
8
@Retention(RetentionPolicy.SOURCE)
9
public @interface MyAnnotationDemo01 {
10
}
11
12
// 例子二=============================
13
package com.yubulang.demo11;
14
15
import java.lang.annotation.Retention;
16
import java.lang.annotation.RetentionPolicy;
17
18
// 注解在java文件编译成为class依然有效
19
@Retention(RetentionPolicy.CLASS)
20
public @interface MyAnnotationDemo02 {
21
}
22
23
// 例子三=============================
24
package com.yubulang.demo11;
25
26
import java.lang.annotation.Retention;
27
import java.lang.annotation.RetentionPolicy;
28
29
// 注解在运行时依然有效
30
@Retention(RetentionPolicy.RUNTIME)
31
public @interface MyAnnotationDemo03 {
32
}

@Documented 说明该注解将包含在 javadoc 中

默认的javadoc工具,是不包括注解的。但是如果声明注解时指定了 @Documented 这个注解就会被 javadoc 之类的工具处理,所以注解类型信息也会被包括在生成文档中,是一个标记注解,没有成员。

@Inherited 子类会继承父类的注解

1
package com.yubulang.demo12;
2
3
import java.lang.annotation.*;
4
5
@Target(ElementType.TYPE)
6
@Retention(RetentionPolicy.RUNTIME)
7
@Inherited
8
@interface InheritedTest {
9
    String value();
10
}
11
12
@InheritedTest("拥有Inherited")
13
class Person {
14
    public void methodOne() {
15
    }
16
17
    public void methodTwo() {
18
    }
19
}
20
21
class Student extends Person {
22
23
}
24
25
@Target(ElementType.TYPE)
26
@Retention(RetentionPolicy.RUNTIME)
27
@interface IsNotInherited {
28
    String value();
29
}
30
31
@IsNotInherited("未拥有Inherited")
32
class NotInheritedAnnotationPerson {
33
    public void methodOne() {
34
    }
35
36
    public void methodTwo() {
37
    }
38
}
39
40
class NotInheritedAnnotationStudent extends NotInheritedAnnotationPerson {
41
42
}
43
44
public class Test02 {
45
    public static void main(String[] args) {
46
        Class<Student> studentClass = Student.class;
47
48
        if (studentClass.isAnnotationPresent(InheritedTest.class)) {
49
            System.out.println(studentClass.getAnnotation(InheritedTest.class).value());
50
        }
51
52
        Class<NotInheritedAnnotationStudent> nStudentClass = NotInheritedAnnotationStudent.class;
53
54
        if (nStudentClass.isAnnotationPresent(IsNotInherited.class)) {
55
            System.out.println(nStudentClass.getAnnotation(IsNotInherited.class).value());
56
        }
57
    }
58
}

自定义注解

  • 使用@interface 自定义注解时,自动继承了 java.lang.annotation.Annotation 接口
  • 分析:
    • @interface 用来声明一个注解,格式:public @interface 注解名 {定义内容}
    • 其中的每一个方法实际上是声明了一个配置参数
    • 方法的名称就是参数的名称
    • 返回值类型就是参数类型(返回值只能是基本类型,Class,String,enum)
    • 可以通过 default 来声明参数的默认值
    • 如果只有一个参数成员,一般命名为 value
    • 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0 作为默认值

知道了这些基础的概念,我觉得还是要通过一些代码,才能巩固。废话就不多说我们看代码。

1
package com.yubulang.demo12;
2
3
import java.lang.annotation.ElementType;
4
import java.lang.annotation.Retention;
5
import java.lang.annotation.RetentionPolicy;
6
import java.lang.annotation.Target;
7
8
public class Test01 {
9
    // 没有 default 默认值,我们必须给注解赋值
10
    // 有 default "" 可以显式赋值,也可以不赋值
11
    @MyAnnotation01(name = "大哥", schools = {"哈理工", "哈工大"})
12
    public void test() {
13
    }
14
15
    @MyAnnotation02("大哥")
16
    public void test02() {
17
    }
18
}
19
20
@Target({ElementType.TYPE, ElementType.METHOD})
21
@Retention(RetentionPolicy.RUNTIME)
22
@interface MyAnnotation01 {
23
    // 注解的参数:参数类型 + 参数名()
24
    String name() default "";
25
26
    int age() default 0;
27
28
    int id() default -1; // 如果默认值为 -1,代表不存在
29
30
    String[] schools() default {};
31
}
32
33
@Target({ElementType.TYPE, ElementType.METHOD})
34
@Retention(RetentionPolicy.RUNTIME)
35
@interface MyAnnotation02 {
36
    String value(); // 方法名为value,才能省略,其他的不行
37
}

总结

知道了怎么定义注解只是一个开始,我们想要注解去处理一些具体的事儿,还需要反射的配合才能实现。万丈高楼平地起,今天就到这里。