java(16):Java Annotation(Java 注解)

目标:掌握 Java Annotation 特性与使用方式

Annotation 工作方式:

    从 Java5.0 版本发布以来,5.0 平台提供了一个正式的 annotation(注解) 功能:允许开发者定义、使用自己的 annotation 类型。此功能由一个定义 annotation 类型的语法和一个描述 annotation 声明的语法、读取 annotation 的 API、一个使用 annotation 修饰的 class 文件以及一个 annotation 处理工具(apt)组成。

    annotation 并不直接影响代码语义,它工作的方式类似程序的工具或者类库一样,可以反过来对正在运行的程序语义有所影响。Annotation 在相应的情况下(下面会说什么样的情况下),可以从源文件、class 文件或者以在运行时反射的多种方式被读取。

JDK 提供的常见注解:

a. Override 注解表示子类要重写(override)父类的对应方法。java.lang.Override 是个 Marker annotation,用于标识的注解,该注解名称本身即表示了要给工具程序提供的信息。(也就是编译器看到了注解的名称,就知道什么意思了)

b. Deprecated 注解表示方法是不建议被使用的。对编译程序说明某个方法已经不建议使用,即该方法是过时的。java.lang.Deprecated 也是个 Marker annotation,Deprecated 这个名称在告知编译程序,被 @Deprecated 标识的方法是一个不建议被使用的方法。

c. SuppressWarnings(xxx) 注解表示抑制警告。对编译程序说明某个方法中若有警告讯息,则加以抑制。

自定义 Annotation 类型:

1.

    关于 Marker Annotation 的定义,也就是 Annotation 名称本身即提供信息。对于程序分析工具来说,主要检查是否有 Marker Annotation 的出现,并作出对应的动作。

2. 

    当注解中的属性名为 value 时,在对其赋值时可以不指定属性的名称而直接写上属性值即可;除了 value 以外的其它值都需要使用 name = value 这种赋值方式,即明确指定给谁赋值,否则报错。

eg:
JDK自定的 SuppressWarnings 注解两种写法:
@SuppressWarnings("unchecked")
@SuppressWarnings(value = "unchecked")

3. 

    我们自定义 Annotation 类型时,注解类型关键字为 @interface。value 成员可以设定默认值,用“default”关键词,并且可以使用数组以及枚举类型等,使用时,多个成员值之间使用逗号隔开。

eg:
package com.cfm.annotation;
public @interface AnnotationTest {
    String value1() default "ym";
    String[] value2();
    EnumTest value3();
}

enum EnumTest{
    YOU, ME, HE
}

4. 

    我们使用 @interface 关键字自定义 Annotation 类型时,实际上是自动继承了 java.lang.annotation.Annotation 接口,由编译器自动为我们完成了其它产生的细节。注意:我们在自定义 Annotation 类型时,不能继承其它的 Annotation 类型或是接口。

5. 

    定义 Annotation 类型时,我们也可以使用包来管理类别,方式类似于类的导入功能。

Annotation 高级特性:

1. 告知编译程序如何处理 @Retention

    java.lang.annotation.Retention 类型可以在您定义 Annotation 类型时,指示编译程序该如何对待您的自定义的 Annotation 类型。缺省(即系统默认状态,意思与默认相同)编译程序会将 Annotation 信息留在 .class 档案中,但不被虚拟机读取,而仅用于编译程序或工具程序运行时提供信息。

    在使用 Retention 类型时,需要提供 java.lang.annotation.RetentionPolicy 的枚举类型。

//  RetentionPolicy 类型源码
public enum RetentionPolicy {

    /**
     * 编译程序不会将 Annotation 储存于 class 文件中,编译程序处理完 Annotation 信息后就完成任务
     */
    SOURCE,

    /**
     * 编译程序将 Annotation 储存于 class 文件中,缺省(默认值),但是不能被 JVM 通过反射获取到。
     */
    CLASS,

    /**
     * 编译程序将 Annotation 储存于 class 文件中,可由 JVM 通过反射获取到。
     */
    RUNTIME
}

2. 

    RetentionPolicySOURCE 的例子:@SuppressWarnings,这个注解在编译期间告知编译器来抑制警告,所以不必将这个信息存储到 .class 文件中。

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, MODULE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
    ...
}

3. 

    RetentionPolicyRUNTIME 的时机,可以像是您使用 Java 设计一个程序代码分析工具,您必须让 VM 能读出 Annotation 信息,以便在分析程序时使用,搭配反射(Reflection)机制,就可以达到这个目的。

4. 

java.lang.reflect.AnnotatedElement 接口:

// 源码

public interface AnnotatedElement {

    /**
     * 判断是否包含参数中的注解类型
     */
    default boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {
    }

   /**
     * 获取参数中的注解类型
     */
    <T extends Annotation> T getAnnotation(Class<T> annotationClass);

    /**
     * 获取该方法或类等所有的包含 @Retention(RetentionPolicy.RUNTIME) 类型的注解。因为只有这种注解才能通过反射机制读取
     */
    Annotation[] getAnnotations();

    /**
     * 获取与参数注解相关联的注解类型
     */
    default <T extends Annotation> T[] getAnnotationsByType(Class<T> annotationClass) {
     }

    /**
     * 如果存在参数中指定类型的注解类型,则返回该元素的注解,否则为空。
     */
    default <T extends Annotation> T getDeclaredAnnotation(Class<T> annotationClass) {
     }

    /**
     * 如果指定类型的注解直接存在或间接存在,则返回该元素的注解类型。
     */
    default <T extends Annotation> T[] getDeclaredAnnotationsByType(Class<T> annotationClass) {
    }

    /**
     * 类似 getAnnotations()
     */
    Annotation[] getDeclaredAnnotations();
}

Class、Constructor、Field、Method、Package等类都实现了 AnnotatedElement 接口,通过此接口提供的方法,我们可以通过反射机制获取相应的注解信息。

5. 

    我们在自定义 Annotation 类型时,必须设定 RetentionPolicy 为 RUNTIME,才能通过反射使用上面 AnnotatedElement 接口提供的相关方法获取相关的 Annotation 信息。

eg:

// 我们自定义的注解类型
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value1() default "ym";
}
// 该类中使用我们上面自定义的注解类型
@MyAnnotation(value1 = "cfm2")
public class MyTest {

    @MyAnnotation(value1 = "cfm2")
    @Deprecated
    @SuppressWarnings("unchecked")
    public void test(){
        System.out.println("my test");
    }
}
// 通过 java 反射机制,使用 java 提供的相关反射类注解相关的 api
public class MyReflection {

    public static void main(String[] args) throws Exception {
        MyTest myTest = new MyTest();
        Class<MyTest> c = MyTest.class;

        Method method = c.getDeclaredMethod("test", new Class[]{});

        // 判断这个方法上面有没有 MyAnnotation 这个注解类型,如果有就执行这个方法
        if(method.isAnnotationPresent(MyAnnotation.class)){
            method.invoke(myTest, new Object[]{});

            // 返回这个注解类型
            MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
            System.out.println(myAnnotation);

            // 看看这个方法上面注解的值
            String value = myAnnotation.value1();
            System.out.println(value);

            // 返回这个方法的所有 @Retention(RetentionPolicy.RUNTIME) 类型注解
            Annotation[] annotations = method.getAnnotations();
            for(Annotation annotation : annotations){
                System.out.println(annotation);
            }
        }
    }
}

6. 

    限定 Annotation 使用的目标 @Target。使用 java.lang.annotation.Target 可以定义该注解类型修饰的目标。在定义的时候要指定 java.lang.annotation.ElementType 的枚举值之一。

ElementType 枚举类型:

7. 

    要求为 API 文件 @Documented。想要在使用者制作 JavaDoc 文件的同时也一并将 Annotation 的信息加入至 API 文件中,使用 java.lang.annotation.Documented。

8. 

    缺省父类别中的 Annotation 并不会被继承至子类别中,可以在定义的 Annotation 类型加上 java.lang.annotation.Inherited 类型的 Annotation,以让父类别中的 Annotation 继承到子类别中。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值