概述

注解相当于一种标签,在程序中加了注解就等于为程序打上了某种标签,javac编译器,开发工具和其他程序可以用反射来了解你的类及各种元素上有无何种标签,看你有什么标记,就去干相应的事。标签可以加在包,类,字段,方法,方法的参数以及局部变量上。


定义

通过 @interface关键字进行定义。

  1. @interface CusAnnotation{
  2. }

注解定义跟接口非常相似,注解前面多了一个 @ 符号。上面的代码就创建了一个名字为 CusAnnotation 的注解。
可以理解为创建了一张名字为 CusAnnotation 的标签

作用

1 作为特殊的标记,用于告诉编译器一些信息
2 编译时动态处理,如动态生成代码
3 运行时动态处理,作为额外信息的载体,如获取注解信息
创建一个类 CustomAnnotation,然后在类定义的地方加上 @CusAnnotation 就可以用 CusAnnotation 注解这个类了。可以简单理解为将 CusAnnotation 这张标签贴到 CustomAnnotation 这个类上面。

  1. @CusAnnotation
  2. public class CustomAnnotation {
  3. }

本质

The common interface extended by all annotation types所有的注解类型都继承自这个普通的接口(Annotation)

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.SOURCE)
  3. public @interface Override {
  4. }

注解 @Override 的定义,其实它本质上就是:一个继承了 Annotation 接口的接口。

  1. public interface Override extends Annotation{
  2. }

内置注解/标准注解

@Override

子类覆盖父类方法或者实现接口的方法

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.SOURCE)
  3. public @interface Override {
  4. }

它没有任何的属性,所以并不能存储任何其他信息。它只能作用于方法之上,编译结束后将被丢弃。
所以你看,它就是一种典型的『标记式注解』,仅被编译器可知,编译器在对 java 文件进行编译成字节码的过程中,一旦检测到某个方法上被修饰了该注解,就会去匹对父类中是否具有一个同样方法签名的函数,如果不是,自然不能通过编译。

  1. public class User {
  2. private String name;
  3. private int age;
  4. public User(String n,int a){
  5. this.name = n;
  6. this.age = a;
  7. }
  8. @Override
  9. public String toString() {
  10. return "current user name:"+this.name+" age:"+this.age;
  11. }
  12. }
  13. public static void main(String[] args) {
  14. User user = new User("tom",20);
  15. System.out.println(user);
  16. }

@Deprecated

表示API已经过时

  1. @Deprecated
  2. public User(String n,int a){
  3. this.name = n;
  4. this.age = a;
  5. }

@SuppressWarnings

如果我们不希望程序启动时,编译器检查代码中过时的方法,就可以使用 @SuppressWarnings 注解并给它的 value 属性传入一个参数值来压制编译器的检查。

  1. @SuppressWarnings("deprecation")
  2. public static void main(String[] args) {
  3. User user = new User("tom",20);
  4. System.out.println(user);
  5. }

自定义注解

元注解

是用于标示注解的注解,元注解可以理解为一种特殊的标签,它的作用和目的就是给其他普通的注解进行解释说明的。JAVA 中有以下『元注解』,应用最多的是Target和Retention

元注解 说明
@Target 注解的作用目标
@Retention 注解的生命周期
@Inherited 是否允许子类继承该注解
@Document 注解是否应当被包含在JavaDoc文档中

@Target

字面解释指的是作用目标,大白话就是指该注解可以用于什么地方,是用来修饰方法?类?还是用来修饰字段属性的。ElementType 参数有:

ElementType 参数名 说明
CONSTRUCTOR 构造器的声明
FIELD 属性字段上(包括enum实例)
LOCAL_VARIABLE 局部变量声明
METHOD 方法声明
PACKAGE 包声明
PARAMETER 参数声明
ANNOTATION_TYPE 允许作用在注解上
TYPE 类、接口(包括注解类型)或enum声明

Target.java源文件

  1. @Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target(ElementType.ANNOTATION_TYPE)
  4. public @interface Target {
  5. ElementType[] value();
  6. }

@Retention

英文意为保留期的意思,类似于标签的时间戳,表示该注解信息时间范围。可选的RetentionPolicy参数包括:

RetentionPolicy参数 说明
SOURCE 当前注解编译期可见,编译后会被丢弃,并不会保留在编译好的.class文件里面
SOURCE 默认生命周期形式,保留在编译好的.class文件里面,无论是类或是方法,乃至字段,他们都是有属性表的,而 JAVA 虚拟机也定义了几种注解属性表用于存储注解信息,但是这种可见性不能带到方法区,类加载时会予以丢弃
RUNTIME 永久保存,在程序运行期间可以通过反射机制读取注解的信息

Retention源文件

  1. @Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target(ElementType.ANNOTATION_TYPE)
  4. public @interface Retention {
  5. RetentionPolicy value();
  6. }

@Override里面的Retention设为SOURCE,编译成功了就不要这一些检查的信息;相反,@Deprecated里面的Retention设为RUNTIME,表示除了在编译时会警告我们使用了哪个被Deprecated的方法,在执行的时候也可以查出该方法是否被Deprecated.

@Inherited

是否允许子类继承该注解,如果一个父类被标记为 @Inherited 注解进行注解的话,如果它的子类没有被任何注解应用的话,那么这个子类就继承了父类的注解。

  1. import java.lang.annotation.Inherited;
  2. import java.lang.annotation.Retention;
  3. import java.lang.annotation.RetentionPolicy;
  4. @Inherited
  5. @Retention(RetentionPolicy.RUNTIME)
  6. @interface Foo {}
  7. @Foo
  8. class SuperBar {
  9. }
  10. public class Bar extends SuperBar {
  11. }

注解 Foo 被 @Inherited 修饰,类 SuperBar 被 Foo 注解,类 Bar 继承 SuperBar,类 Bar 也拥有 Foo 这个注解。

@Documented

是否允许子类继承父类中的注解

image.png

  1. package cn.bx.annotation;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. import java.lang.reflect.Method;
  7. @Target(value = {ElementType.METHOD, ElementType.TYPE})
  8. @Retention(RetentionPolicy.RUNTIME)
  9. @interface Description {
  10. String foo();
  11. }
  12. @Description(foo = "hello class ")
  13. class UseAnnotation {
  14. @Description(foo = "hello method ")
  15. public void hello() {
  16. System.out.println("hello world");
  17. }
  18. }
  19. public class ParseAnnotation {
  20. public static void main(String[] args) {
  21. Class<UseAnnotation> useAnnotationClass = UseAnnotation.class;
  22. boolean annotationPresent = useAnnotationClass.isAnnotationPresent(Description.class);
  23. if (annotationPresent) {
  24. Description annotation = useAnnotationClass.getAnnotation(Description.class);
  25. System.out.println(annotation.foo());
  26. }
  27. Method[] methods = useAnnotationClass.getMethods();
  28. for (Method method : methods) {
  29. if (method.isAnnotationPresent(Description.class)) {
  30. Description annotation = method.getAnnotation(Description.class);
  31. System.out.println(annotation.foo());
  32. }
  33. }
  34. }
  35. }

打印结果

  1. hello class
  2. hello method