Java基础之注解的使用

前言

注解在JDK源码、Spring源码、企业项目中都是运用的非常广泛,JDK源码中比较常见的有@Override、@Deprecated、@SuppressWarnings。我将系统性的介绍一下注解,以及注解的使用。

 

什么是注解

我们对@Override已经很熟悉了,下面我们点来这个注解的定义。代码如下所示:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

我们再来看一下@SuppressWarinings。

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

    String[] value();
}

定义在最上方的@Target、@Retention又称为元注解。限制了自定义注解@Override的特性。

元注解:元注解是可以注解到注解上的注解,它是一种基本注解,能够应用到其他注解上的注解。如果注解是一个标签,那么元注解是一个特殊的标签,他的目的和作用就是为了给普通标签进行解释说明的。元注解有五种,分别是:@Retention、@Documented、@Target、@Inherited、@Repeatable.

①@Retention是保留期之意,即使用了这个元注解的注解的存活时间。取值有三种RetentionPolicy.SOURCE,RetentionPolicy.CLASS,RetentionPolicy.RUNTIME。自定义注解的元注解Retention取值为SOURCE表明只在源码阶段保留,在编译器进行编译时,这个自定义注解就会被忽略掉。取值为CLASS表明自定义注解只保留到编译进行的时候,不会被加载到JVM中。取值为RUNTIME表明自定义注解可以保留到程序运行时期,它会被加载到JVM中,因此程序可以在运行阶段获取到。这个元注解的三个枚举值的生命周期依次变长。

②@Documented是保存到文档,即使用了这个元注解的注解。能够在将注解的元素包含到Javadoc中。

③@Target是目标之意,即表明了自定义注解在什么地方能够使用,类上?方法上?变量上?取值也是一个枚举ElementType。枚举中的元素包含ANNOTATION_TYPE-可以在注解上进行注解,CONSTRUCTOR -可以给构造函数进行注解。FIELD -可以给成员属性进行注解。LOCAL_VARIABLE 可以给局部变量进行注解。METHOD 可以给方法进行注解。PACKAGE 可给一个包进行注解。PARAMETER 可以在方法内的形式参数进行注解。TYPE 可以给一个类型进行注解,比如类,接口,枚举等等。

④@Inherited继承之意,如果一个超类应用了某一个注解,它的子类没有被其他任何注解应用的话,那么这个子类就继承了超类的注解。

⑤@Repeatable,同一个地方,可以使用多个形同的注解。

 

 在注解中往往会拥有一些元素,比如上面提到的SuppressWarnings中的values;@Override中不存在任何元素,因此我们把@Override叫作标记注解。

 

注解中的可用类型包括:所有的基本数据类型、String、Class、enum、Annotation;所有的基本数据类型、String、Class、enum、Annotation类型的数组形式。同时元素之中不能存在右不确定的值,可以设置默认值,或者在注解使用时赋值元素的值,并且,元素不能使用null作为默认值。如果元素定义为value,在使用时可以将value="xxx",省略写成“xxx”。

 

最佳实践

在《think in java》一书注解章节中是这么描述的:注解(也被称为元数据)为我们在代码中添加信息提供了一种形式化的的方法,使我们可以在稍后的某个时刻非常方便的地使用这些数据。注解中的重点是定义注解,解析注解,使用注解。如下所示我们自定义一个注解UseAnnotation。

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;

@Target({METHOD, CONSTRUCTOR, FIELD, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface UseAnnotation {



}

因为UseAnnotation中没有任何一个元素,因此它是一个标记注解。该注解的可见范围是在运行时期。目标区域是在方法上,构造函数上,局部变量上 。这里稍微提一下,target这个元注解的枚举值中的元素没有采用[类名].[元素名]的格式,而是直接引入元素。是因为在类的上方采用了静态导包的方式,import static java.lang.annotation.ElementType.*;这里使用的是【*】,如果仅仅想要导入某一个类的某一个元素,可以导入例如import static java.lang.annotation.ElementType.METHOD;

下面我们在注解UseAnnotation增加几个元素。

 1 import static java.lang.annotation.ElementType.*;
 2 
 3 @Target({METHOD, CONSTRUCTOR, FIELD, LOCAL_VARIABLE})
 4 @Retention(RetentionPolicy.RUNTIME)
 5 public @interface UseAnnotation {
 6 
 7     int id() default 0;
 8     String name();
 9     long value() default 0L;
10     Class clazz() default Object.class;
11     Target[] annotation() default {@Target({METHOD}),@Target({METHOD, CONSTRUCTOR})};
12 ElementType[] enums();
13 }

 注意元素的类型只能使用基本数据类型,String,Class、enum、Annotation,或者这些类型的数组类型。像Long  Integer编译报错。如上代码,可以定义为int、long、String、Class、Target[]、ElementType[] 。同时默认值不能设置为null。在项目中我们可以采用-1,空字符串来代替。接下来我们做一下注解的使用:

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
import java.util.HashMap;

public class UseAnnotationExample {


    @UseAnnotation(name="Tom",enums = {ElementType.CONSTRUCTOR} )
    public void test1() {

    }

    @UseAnnotation(id = 100, name="jack",value = 200L, enums = {ElementType.METHOD})
    public void test2() {

    }

    @UseAnnotation(id = 1000, name="lucy",clazz = HashMap.class,annotation={@Target(value=ElementType.ANNOTATION_TYPE)}, enums = {ElementType.LOCAL_VARIABLE, ElementType.FIELD})
    public void test3() {

    }
}

 需要主要的是,如果在注解声明时,定义了默认值,那么使用时可以不用传,另外在注解声明时默认值不可以使用null,在使用时也不能使用null。下面笔者演示一下注解UseAnnotation的解析:

 1 public static void parse () {
 2         Class<UseAnnotationExample> clazz = UseAnnotationExample.class;
 3 
 4         Method[] methods = clazz.getMethods();
 5 
 6         for(Method method : methods) {
 7             if(method == null) {
 8                 continue;
 9             }
10 
11             UseAnnotation annotation = method.getAnnotation(UseAnnotation.class);
12 
13             if(annotation == null) {
14                 continue;
15             }
16 
17             System.out.println(method.getName()+annotation.id()+annotation.clazz().getName());
18 
19         }
20     }

运行结果如下所示:

test10java.lang.Object
test2100java.lang.Object
test31000java.util.HashMap

注解的实践就到这里。

posted @ 2018-10-04 14:55  冰糖小城  阅读(395)  评论(0编辑  收藏  举报