注解简而言之就是注释,放在类、方法、字段、参数前。

java注解分类

1、编译器使用的注解:

@Override:让编译器检查改方法是否正确地实现了覆写。

@SuppressWarning:告诉编译器忽略此处代码产生的警告。

这种注解不会被编译进入.class文件。

2、工具处理.clas文件使用的注解:

这类注解会被编译进入.class文件,但加载结束后并不会存在于内存中。

3、程序运行期能够读取的注解:

它们在加载后一直存在于JVM中,这也是最常用的注解。

定义一个注解时,还可以定义配置参数。配置参数可以包括:

  • 所有基本类型;
  • String;
  • 枚举类型;
  • 基本类型、String、Class以及枚举的数组。

因为配置参数必须是常量,所以,上述限制保证了注解在定义时就已经确定了每个参数的值。

举个栗子,对以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
publicclassHello {@Check(min=0, max=100, value=55)
publicint n;

@Check(value=99)
publicint p;

@Check(99)// @Check(value=99)publicint x;

@Check
publicint y;
}

@Check就是一个注解。第一个@Check(min=0, max=100, value=55)明确定义了三个参数,第二个@Check(value=99)只定义了一个value参数,它实际上和@Check(99)是完全一样的。最后一个@Check表示所有参数都使用默认值。

如何定义注解呢?

Java语言使用@interface语法来定义注解(Annotation),它的格式如下:

1
2
3
4
5
public @interface Report {
int type()default 0;
String level()default "info";
String value()default "";
}

注解的参数类似无参数方法,可以用default设定一个默认值(强烈推荐)。最常用的参数应当命名为value

元注解:

用于修饰其他注解的注解。

@Target

最常用的元注解是@Target。使用@Target可以定义Annotation能够被应用于源码的哪些位置:

  • 类或接口:ElementType.TYPE
  • 字段:ElementType.FIELD
  • 方法:ElementType.METHOD
  • 构造方法:ElementType.CONSTRUCTOR
  • 方法参数:ElementType.PARAMETER

例如,定义注解@Report可用在方法上,我们必须添加一个@Target(ElementType.METHOD)

1
2
3
4
5
6
@Target(ElementType.METHOD)
public @interface Report {
int type() default 0;
String level() default "info";
String value() default "";
}

定义注解@Report可用在方法或字段上,可以把@Target注解参数变为数组{ ElementType.METHOD, ElementType.FIELD }

1
2
3
4
5
6
7
@Target({
ElementType.METHOD,
ElementType.FIELD
})
public @interface Report {
...
}

@Retention

另一个重要的元注解@Retention定义了Annotation的生命周期:

  • 仅编译期:RetentionPolicy.SOURCE
  • 仅class文件:RetentionPolicy.CLASS
  • 运行期:RetentionPolicy.RUNTIME

如果@Retention不存在,则该Annotation默认为CLASS。因为通常我们自定义的Annotation都是RUNTIME,所以,务必要加上@Retention(RetentionPolicy.RUNTIME)这个元注解:

1
2
3
4
5
6
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {
int type()default 0;
String level()default "info";
String value()default "";
}

(还有一些其他的注解,比如repeatable,和inherent,这些看名字差不多能知道是什么用)

如何定义Annotation

我们总结一下定义Annotation的步骤:

第一步,用@interface定义注解:

1
2
3
public @interface Report {
}

第二步,添加参数、默认值:

1
2
3
4
5
6
public @interface Report {
int type()default 0;
String level()default "info";
String value()default "";
}

把最常用的参数定义为value(),推荐所有参数都尽量设置默认值。

第三步,用元注解配置注解:

1
2
3
4
5
6
7
8
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Report {
int type()default 0;
String level()default "info";
String value()default "";
}

其中,必须设置@Target@Retention@Retention一般设置为RUNTIME,因为我们自定义的注解通常要求在运行期读取。一般情况下,不必写@Inherited@Repeatable

如何处理注解

RUNTIME注解被使用的最多,读取注解需要使用反射API

判断某个注解是否存在于ClassFieldMethodConstructor

  • Class.isAnnotationPresent(Class)
  • Field.isAnnotationPresent(Class)
  • Method.isAnnotationPresent(Class)
  • Constructor.isAnnotationPresent(Class)

例如:

1
2
// 判断@Report是否存在于Person类:
Person.class.isAnnotationPresent(Report.class);

使用反射API读取Annotation:

  • Class.getAnnotation(Class)
  • Field.getAnnotation(Class)
  • Method.getAnnotation(Class)
  • Constructor.getAnnotation(Class)
1
2
3
4
// 获取Person定义的@Report注解:
Report report = Person.class.getAnnotation(Report.class);
int type = report.type();
String level = report.level();

读取方法的数组需要用到二维数组,暂略

如何使用注解

首先定义注解,然后使用注解,同时需要写一个check函数用来检查注解。

小结

注解(Annotation)是Java语言用于工具处理的标注:

注解可以配置参数,没有指定配置的参数使用默认值;

如果参数名称是value,且只有一个参数,那么可以省略参数名称。

Java使用@interface定义注解:

可定义多个参数和默认值,核心参数使用value名称;

必须设置@Target来指定Annotation可以应用的范围;

应当设置@Retention(RetentionPolicy.RUNTIME)便于运行期读取该Annotation