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
注解的实践就到这里。