Annotation 是 Java1.5 推出的新特性。它用来描述 Java 中的代码,如类、方法、字段等等。所以也把注解称之为元数据。
Java1.5 内置的常见的注解:
- @Deprecated
- @Override
- @Deprecated
使用 Java 内置的 @Deprecated 注解,举个例子:
@Deprecated
public void test(){
// do something
}
上面的 @Deprecated 注解使用在 Java 的方法上,表示说该方法已经废弃了,不建议使用者调用。
除了 @Deprecated 注解还有 @Override,表示该方法是重写父类的方法:
public class AnnotationTest {
@Override
public String toString() {
return super.toString();
}
}
在 JDK1.5 中除了内置上面两个注解外,还有 @SuppressWarnings,该注解主要用于抑制编译器警告,例如:
@SuppressWarnings("unchecked")
public void testWarning() {
List list = new ArrayList();
list.add("a");
list.add("b");
list.add("c");
}
自定义注解
在自定义注解之前,我们可以看下 JDK 内置的注解的源码,看看系统是怎么定义注解的,以 @Deprecated 为例:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
public @interface Deprecated {
}
我们发现,Java 是通过 @interface 关键字来定义注解的。
除此以外,我们还发现,在 @Deprecated 注解上还有 @Documented、@Target、@Retention 注解,这些注解用来描述 @Deprecated 注解的,我们把它称之为元注解。
什么是元注解呢?就是描述注解的注解。什么是元数据呢?就是描述数据的数据。
@Documented 元注解
用来描述目标注解将被 javadoc 工具提取成文档。
例如在 AnnotationTest 类上使用 @Deprecated 注解:
@Deprecated
public class AnnotationTest {
}
然后使用 javadoc 命令来生成文档,然后查看生成的文档 html :
我们发现 @Deprecated 被提取到 API 文档上了。
我们可以使用 @SuppressWarnings 注解来描述 AnnotationTest:
@SuppressWarnings("unchecked")
public class AnnotationTest {
}
@SuppressWarnings 注解没有被 @Documented 元注解修饰,所以 API 文档上不会有该注解。
通过 javadoc 命令重新生成 API 文档:
@Target 元注解
描述目标注解用在哪个地方,比如:是用在方法上,还是用在类上。具体有哪些地方呢? Java 通过枚举列举出来了:
public enum ElementType {
// 类、接口、枚举
TYPE,
// 字段
FIELD,
// 方法
METHOD,
// 参数
PARAMETER,
// 构造函数
CONSTRUCTOR,
// 局部变量
LOCAL_VARIABLE,
// 注解
ANNOTATION_TYPE,
// 包
PACKAGE,
}
@Retention 元注解
用于描述目标注解保留在什么阶段。主要有三个阶段,定义在 RetentionPolicy 枚举类中:
public enum RetentionPolicy {
// 注解只保留在源码中,编译成 class 后就不存在了
SOURCE,
// 注解保留在 class 字节码中,这是默认值
CLASS,
// 注解保留到运行时,程序运行的时候也可以获取到该注解
RUNTIME
}
@Inherited 元注解
除了上面几个元注解,还有 @Inherited 元注解,该元注解表示目标注解具有继承性,听起来还是不好理解,下面通过例子来说明下:
首先我们自定义一个注解(该注解被 @Inherited 元注解修饰):
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface BaseAnnotation {
}
然后新建一个类 BaseClass :
@BaseAnnotation
class BaseClass {
}
然后再新建一个类,然后继承上面的 BaseClass :
public class InheritedTest extends BaseClass {
public static void main(String[] args) {
// InheritedTest 有没有被 BaseAnnotation 注解修饰?
System.out.println(InheritedTest.class.isAnnotationPresent(BaseAnnotation.class));
}
}
上面的程序输出 true。
虽然我们的 InheritedTest 类并没有显式的被 BaseAnnotation 修饰,但是它的父类 BaseClass 被 BaseAnnotation 注解修饰了,又因为 BaseAnnotation 注解被元注解 @Inherited 修饰,所以具备继承性。InheritedTest 继承了 BaseClass,也把 BaseAnnotation 注解继承过来了。我们可以把 @Inherited 注解去掉,在运行程序,会输出 false,因为这个时候 BaseAnnotation 没有继承性了。
注解与反射
关于注解与反射相关的内容可以查看我的博客:《Java 核心基础(二)反射技术详解》