文章

Java注解

Java注解

注解

什么是注解

Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。 注解是元数据的一种形式,提供有关
于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。

注解的作用

  1. 标记,如告诉编译器一些信息
  2. 编译时动态处理,如动态生成代码
  3. 运行时动态处理,如得到注解信息

声明一个注解类型

Java 中所有的注解,默认实现`Annotation 接口:

1
2
3
4
5
6
7
package java.lang.annotation;
public interface Annotation {
    boolean equals(Object obj);
    int hashCode();
    String toString();
    Class<? extends Annotation> annotationType();
}

与声明一个 “Class” 不同的是,注解的声明使用 @interface 关键字。一个注解的声明如下:

1
2
public @interface Lance{
}

元注解(4 个)

在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为 metaannotation(元注解)。

元注解指的是用来定义注解的注解

@Retention

@Retention 注解指定标记注解的存储方式,保留时间,可选值 SOURCE(源码时),CLASS(编译时),RUNTIME(运行时),默认为 CLASS

  1. SOURCE 这个 Annotation 的信息保留在程序源代码里,源码如果经过了编译之后,Annotation 的数据就会消失,并不会保留在编译好的.class 文件里面。大都为 Mark Annotation,这类 Annotation 大都用来校验,比如 Override,Deprecated,SuppressWarnings
  2. CLASS 这个 Annotation 类型的信息保留在程序源码里,同时也会保留在编译好的.class 文件里面,在执行的时候,并不会把这一些信息加载到虚拟机 (JVM) 中去.注意一下,当你没有设定一个 Annotation 类型的 Retention 值时,系统默认值是 CLASS。应用 APT?
  3. RUNTIME 在源码、编译好的.class 文件中保留信息,在执行的时候会把这一些信息加载到 JVM 中去的。比如,运行时 Ioc 框架利用反射获取某个对象上的注解信息

@Target

注解标记另一个注解,以限制可以应用注解的 Java 元素类型。目标注解指定以下元素类型之一作为其值:

1
2
3
4
5
6
7
8
ElementType.ANNOTATION_TYPE 可以应用于注解类型
ElementType.CONSTRUCTOR 可以应用于构造函数
ElementType.FIELD 可以应用于字段或属性
ElementType.LOCAL_VARIABLE 可以应用于局部变量
ElementType.METHOD 可以应用于方法级注解
ElementType.PACKAGE 可以应用于包声明
ElementType.PARAMETER 可以应用于方法的参数
ElementType.TYPE 可以应用于类的任何元素

@Inherited

@Inherited 是否可以被继承,默认为 false。 该注解可以被自动继承。如果自定义注解声明了@Inherited,子类就会沿着继承树去查找有改表示的注解。除了在类上使用外,用在其他地方的都不起作用。只能在继承的情况下起作用,如果实现一个借口@Inherited 也是不起作用的

@Documented

@Documented 是否会保存到 Javadoc 文档中,用于被 javadoc 工具提取成文档

标准注解(3 个)

Java 自带的几个注解

@Override

@Override 重写函数

@Deprecated

@Deprecated 废弃,不鼓励使用 (有更好方式、使用有风险或已不在维护)

@SuppressWarnings

@SuppressWarnings 忽略某项 Warning

自定义注解以及属性定义

  • 1、通过 @interface 定义,注解名即为自定义注解名
  • 2、声明注解的属性
    注解属性的作用:原来写在配置文件中的信息,可以通过注解的属性进行描述。
    • a) Annotation 属性声明方式:String name();
    • b) Annotation 属性默认值声明方式:String name() default "xxx";
    • c) Annotation 属性的特殊属性 value:如果注解中有一个名称 value 的属性,那么使用注解时可以省略 value=部分:@MyAnnotation("xxx")
    • d) 还有一个特殊属性 value[];和 value 属性一样,可以省略,只写值,多个字用大括号{}包起来,如:@MyAnnotation3({"hacket","hacket2"});如果数组属性中只有一个元素,这时候属性值部分可以省略大括号 如:@MyAnnotation3("hacket")

总结:

  • (1) 通过 @interface 定义,注解名即为自定义注解名
  • (2) 注解配置参数名为注解类的方法名,且:
  • a. 所有方法没有方法体,没有参数没有修饰符,实际只允许 public & abstract 修饰符,默认为 public ,不允许抛异常
  • b. 方法返回值只能是基本类型,String, Class, annotation, enumeration 或者是他们的一维数组
  • c. 若只有一个默认属性,可直接用 value() 函数。一个属性都没有表示该 Annotation 为 Mark Annotation
  • (3). 可以加 default 表示默认值
1
2
3
4
5
6
7
8
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation1 {
    String name(); // 注解属性声明
    int age();
    String gender() default "male"; // 注解属性的默认值
    MyAnnotation2[] initParam(); // 注解属性为注解类型的数组
}
1
2
3
4
5
6
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation2 {
    String paramName() default "";
    String paramValue() default "";
}
1
2
3
4
5
6
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation3 {
//    String value();
//    int value();
    String[] value();
}

注解反射

具体的 api

1
2
3
method.getAnnotation(AnnotationName.class); // 得到该 Target 某个 Annotation 的信息,因为一个 Target 可以被多个 Annotation 修饰
method.getAnnotations(); // 得到该 Target 所有 Annotation
method.isAnnotationPresent(AnnotationName.class); // 表示该 Target 是否被某个 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
public class Test {

    // @MyAnnotation1(name = "hacket", age = 1, initParam = {@MyAnnotation2(paramName = "paramName", paramValue = "paramValue") })

    // @MyAnnotation3("hacket")
    // @MyAnnotation3(22)
    @MyAnnotation3({ "hacket", "hacket2" })
    
    @MyAnnotation1(name = "hacket", age = 1, initParam = { @MyAnnotation2(paramName = "paramName", paramValue = "paramValue") })
    static class TestAnnotaion {

        @MyAnnotation2(paramName = "hacket2", paramValue = "value2")
        void test(String param, String value) {
            System.out.println("test..." + param + ":" + value);
        }

        void test2() {
            System.out.println("test2...");
        }
    }

    public static void main(String[] args) throws Exception {
        // 注解是否存在
        boolean present = TestAnnotaion.class
                .isAnnotationPresent(MyAnnotation1.class);
        System.out.println(present);
        
        boolean present3 = TestAnnotaion.class
                .isAnnotationPresent(MyAnnotation3.class);
        System.out.println(present3);

        Method[] methods = TestAnnotaion.class.getDeclaredMethods();
        for (Method method : methods) {
            boolean present2 = method.isAnnotationPresent(MyAnnotation2.class);
            if (present2) {
                MyAnnotation2 annotation = method
                        .getAnnotation(MyAnnotation2.class); // 看方法上是否有MyAnnotation2注解
                String paramName = annotation.paramName(); // 得到MyAnnotation2注解属性上的值
                String paramValue = annotation.paramValue();
                method.invoke(TestAnnotaion.class.newInstance(), paramName,
                        paramValue);
            }
        }

    }
}

结果:

1
2
3
true
true
test...hacket2:value2

注解继承

父类的类上和方法上有自定义注解,子类继承父类后的情况:

 自定义注解未写 @Inherited自定义注解写了 @Inherited
子类类上能否继承到父类的类上注解
子类方法,重写了父类的抽象方法,这个方法能否继承到注解
子类方法,继承了父类上的方法,这个方法能否继承到注解
子类方法,覆盖了父类上的方法,这个方法能否继承到注解

@Inherited 修饰的注解只能作用于类上有效,作用在方法上不能继承;@Inherited 修饰的注解只能作用类上,在接口上继承失效

自定义注解

一、自定义 Annotation

  • 1、定义新的 Annotation 类型使用@interface 关键字
  • 2、声明注解的属性

注解属性的作用:原来写在配置文件中的信息,可以通过注解的属性进行描述。

  • a) Annotation 属性声明方式:String name();
  • b) Annotation 属性默认值声明方式:String name() default "xxx";
  • c) Annotation 属性的特殊属性 value:如果注解中有一个名称 value 的属性,那么使用注解时可以省略 value=部分:@MyAnnotation("xxx")
  • d) 还有一个特殊属性 value[];和 value 属性一样,可以省略,只写值,多个字用大括号{}包起来

注意: 如果自定义注解中只有一个属性 value 或者一个 value 数组,如果有多个值,那么 value 不能省略

二、案例

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
public @interface MyAnnotation1 {

    String name(); // 注解属性声明

    int age();

    String gender() default "male"; // 注解属性的默认值

    MyAnnotation2[] initParam(); // 注解属性为注解类型的数组
}

public @interface MyAnnotation2 {

    String paramName() default "";

    String paramValue() default "";
}

public @interface MyAnnotation3 {
//    String value();
//    int value();
    String[] value();
}

// 应用了注解
public class Test {

    @MyAnnotation1(name = "hacket", age = 1, initParam = {   @MyAnnotation2(paramName = "paramName", paramValue = "paramValue") })
    
    // @MyAnnotation3("hacket")
    // @MyAnnotation3(22)
    @MyAnnotation3({ "hacket", "hacket2" })
    
}
本文由作者按照 CC BY 4.0 进行授权