Java注解
Javadoc(文档注释)
- Java 支持 3 种注释,分别是单行注释、多行注释和文档注释。文档注释以/*开头,并以/结束,可以通过 Javadoc 生成 API 帮助文档,Java 帮助文档主要用来说明类、成员变量和方法的功能。
- 文档注释只放在类、接口、成员变量、方法之前,因为 Javadoc 只处理这些地方的文档注释,而忽略其它地方的文档注释。
- Javadoc 是 Sun 公司提供的一种工具,它可以从程序源代码中抽取类、方法、成员等注释,然后形成一个和源代码配套的 API 帮助文档。也就是说,只要在编写程序时以一套特定的标签注释,在程序编写完成后,通过 Javadoc 就形成了程序的 API 帮助文档。
- API 帮助文档相当于产品说明书,而说明书只需要介绍那些供用户使用的部分,所以 Javadoc 默认只提取 public、protected 修饰的部分。如果要提取 private 修饰的部分,需要使用 -private。
Javadoc标签
Javadoc 工具可以识别文档注释中的一些特殊标签,这些标签一般以@开头,后跟一个指定的名字,有的也以{@开头,以}结束。 | 标签 | 描述 | 示例 | | :—- | :—- | :—- | | @author | 标识一个类的作者,一般用于类注释 | @author description | | @deprecated | 指名一个过期的类或成员,表明该类或方法不建议使用 | @deprecated description | | {@docRoot} | 指明当前文档根目录的路径 | Directory Path | | @exception | 可能抛出异常的说明,一般用于方法注释 | @exception exception-name explanation | | {@inheritDoc} | 从直接父类继承的注释 | Inherits a comment from the immediate surperclass. | | {@link} | 插入一个到另一个主题的链接 | {@link name text} | | {@linkplain} | 插入一个到另一个主题的链接,但是该链接显示纯文本字体 | Inserts an in-line link to another topic. | | @param | 说明一个方法的参数,一般用于方法注释 | @param parameter-name explanation | | @return | 说明返回值类型,一般用于方法注释,不能出现再构造方法中 | @return explanation | | @see | 指定一个到另一个主题的链接 | @see anchor | | @serial | 说明一个序列化属性 | @serial description | | @serialData | 说明通过 writeObject() 和 writeExternal() 方法写的数据 | @serialData description | | @serialField | 说明一个 ObjectStreamField 组件 | @serialField name type description | | @since | 说明从哪个版本起开始有了这个函数 | @since release | | @throws | 和 @exception 标签一样 | . The @throws tag has the same meaning as the @exception tag. | | {@value} | 显示常量的值,该常量必须是 static 属性。 | Displays the value of a constant, which must be a static field. | | @version | 指定类的版本,一般用于类注释 | @version info |
对两种标签格式的说明:
- @tag 格式的标签(不被{ }包围的标签)为块标签,只能在主要描述(类注释中对该类的详细说明为主要描述)后面的标签部分(如果块标签放在主要描述的前面,则生成 API 帮助文档时会检测不到主要描述)。
- {@tag} 格式的标签(由{ }包围的标签)为内联标签,可以放在主要描述中的任何位置或块标签的注释中。
Javadoc 标签注意事项:
- Javadoc 标签必须从一行的开头开始,否则将被视为普通文本。
- 一般具有相同名称的标签放在一起。
- Javadoc 标签区分大小写,代码中对于大小写错误的标签不会发生编译错误,但是在生成 API 帮助文档时会检测不到该注释内容。
文档注释的格式
- 在编写文档注释的过程中,有时需要添加 HTML 标签,比如:需要换行时,应该使用
,而不是一个回车符;需要分段时,应该使用。
- Javadoc 并不是将代码中的文档注释直接复制到帮助文档的 HTML 文件中,而是读取每一行后,删除前面的号及以前的空格再输入到 HTML 文档。
- 示例```java /**
- first line. * second line.
- third line. */ ```
- 在编写文档注释的过程中,有时需要添加 HTML 标签,比如:需要换行时,应该使用
- 编译输出后的 HTML 源码```
first line.
second line.
third line.
- 注释前面的_号允许连续使用多个,其效果和使用一个_号一样,但多个_前不能有其他字符分隔,否则分隔符及后面的_号都将作为文档的内容。
<a name="1e0b3304"></a>
## Java架构
![](./JavaAnnotation/Annotation%E6%9E%B6%E6%9E%84.jpg#alt=)
- 1 个 Annotation 和 1 个 RetentionPolicy 关联。可以理解为:每1个Annotation对象,都会有唯一的RetentionPolicy属性。
- 1 个 Annotation 和 1~n 个 ElementType 关联。可以理解为:对于每 1 个 Annotation 对象,可以有若干个 ElementType 属性。
- Annotation 有许多实现类,包括:Deprecated, Documented, Inherited, Override 等等。**Annotation 的每一个实现类,都 "和 1 个 RetentionPolicy 关联" 并且 " 和 1~n 个 ElementType 关联"**。
<a name="abb76d97"></a>
## Annotation 组成部分
1. 三个类
```java
public interface Annotation {
boolean equals(Object obj);
int hashCode();
String toString();
Class<? extends Annotation> annotationType();
}
public enum ElementType {
TYPE, /* 类、接口(包括注释类型)或枚举声明 */
FIELD, /* 字段声明(包括枚举常量) */
METHOD, /* 方法声明 */
PARAMETER, /* 参数声明 */
CONSTRUCTOR, /* 构造方法声明 */
LOCAL_VARIABLE, /* 局部变量声明 */
ANNOTATION_TYPE, /* 注释类型声明 */
PACKAGE /* 包声明 */
}
public enum RetentionPolicy {
SOURCE, /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了 */
CLASS, /* 编译器将Annotation存储于类对应的.class文件中。默认行为 */
RUNTIME /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
}
- 关联解释
Annotation 就是个接口。
- “每 1 个 Annotation” 都与 “1 个 RetentionPolicy” 关联,并且与 “1~n 个 ElementType” 关联。可以通俗的理解为:每 1 个 Annotation 对象,都会有唯一的 RetentionPolicy 属性;至于 ElementType 属性,则有 1~n 个。
ElementType 是 Enum 枚举类型,它用来指定 Annotation 的类型。
- “每 1 个 Annotation” 都与 “1~n 个 ElementType” 关联。当 Annotation 与某个 ElementType 关联时,就意味着:Annotation有了某种用途。
- 例如,若一个 Annotation 对象是 METHOD 类型,则该 Annotation 能用来修饰方法。
RetentionPolicy 是 Enum 枚举类型,它用来指定 Annotation 的策略。
- 通俗点说,就是不同 RetentionPolicy 类型的 Annotation 的作用域不同。
“每 1 个 Annotation” 都与 “1 个 RetentionPolicy” 关联。
若 Annotation 的类型为 SOURCE,则意味着:Annotation 仅存在于编译器处理期间,编译器处理完之后,该 Annotation 就没用了。
- 例如,” @Override” 标志就是一个 Annotation。当它修饰一个方法的时候,就意味着该方法覆盖父类的方法;并且在编译期间会进行语法检查!编译器处理完后,”@Override” 就没有任何作用了。
- 若 Annotation 的类型为 CLASS,则意味着:编译器将 Annotation 存储于类对应的 .class 文件中,它是 Annotation 的默认行为。
- 若 Annotation 的类型为 RUNTIME,则意味着:编译器将 Annotation 存储于 class 文件中,并且可由JVM读入。
Annotation 通用定义
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
}
- 定义一个 Annotation,它的名字是 MyAnnotation
- 可以在代码中通过 “@MyAnnotation” 来使用它
-
- 使用 @interface 定义注解时,意味着它实现了 java.lang.annotation.Annotation 接口,即该注解就是一个Annotation。
- 定义 Annotation 时,@interface 是必须的
- 注意:它和我们通常的 implemented 实现接口的方法不同。
- Annotation 接口的实现细节都由编译器完成。
- 通过 @interface 定义注解后,该注解不能继承其他的注解或接口。
-
- 类和方法的 Annotation 在缺省情况下是不出现在 javadoc 中的。如果使用 @Documented 修饰该 Annotation,则表示它可以出现在 javadoc 中。
- 定义 Annotation 时,@Documented 可有可无;
- 若没有定义,则 Annotation 不会出现在 javadoc 中。
-
- ElementType 是 Annotation 的类型属性。而 @Target 的作用,就是来指定 Annotation 的类型属性。
- @Target(ElementType.TYPE) 的意思就是指定该 Annotation 的类型是 ElementType.TYPE。这就意味着,MyAnnotation1 是来修饰”类、接口(包括注释类型)或枚举声明”的注解。
- 定义 Annotation 时,@Target 可有可无。若有 @Target,则该 Annotation 只能用于它所指定的地方;若没有 @Target,则该 Annotation 可以用于任何地方。
@Retention(RetentionPolicy.RUNTIME)
- RetentionPolicy 是 Annotation 的策略属性,而 @Retention 的作用,就是指定 Annotation 的策略属性。
- @Retention(RetentionPolicy.RUNTIME) 的意思就是指定该 Annotation 的策略是 RetentionPolicy.RUNTIME。这就意味着,编译器会将该 Annotation 信息保留在 .class 文件中,并且能被虚拟机读取。
- 定义 Annotation 时,@Retention 可有可无。若没有 @Retention,则默认是 RetentionPolicy.CLASS。
- @interface 用来声明 Annotation,@Documented 用来表示该 Annotation 是否会出现在 javadoc 中, @Target 用来指定 Annotation 的类型,@Retention 用来指定 Annotation 的策略。
Java基本注解
作用在其他注解的注解(元注解)
- @Retention(value = RetentionPolicy.RUNTIME)
- @Target(value = { ElementType.TYPE })
- @Documented
- @Inherited :只能被用来标注“Annotation类型”,它所标注的Annotation具有继承性。
java @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Inherited { }
作用在代码的注解
- @Override :检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
- @Deprecated :标记过时方法。如果使用该方法,会报编译警告。
java @Documented @Retention(RetentionPolicy.RUNTIME) public @interface Deprecated { }
- @SuppressWarnings :指示编译器去忽略注解中声明的警告。
java @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
- String[] value(); 意味着,SuppressWarnings 能指定参数
- SuppressWarnings 的作用是,让编译器对"它所标注的内容"的某些警告保持静默, @SuppressWarnings(value={"deprecation", "unchecked"})
deprecation -- 使用了不赞成使用的类或方法时的警告
unchecked -- 执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型。
fallthrough -- 当 Switch 程序块直接通往下一种情况而没有 Break 时的警告。
path -- 在类路径、源文件路径等中有不存在的路径时的警告。
serial -- 当在可序列化的类上缺少 serialVersionUID 定义时的警告。
finally -- 任何 finally 子句不能正常完成时的警告。
all -- 关于以上所有情况的警告。
额外添加了 3 个注解:
- @SafeVarargs :Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
- @FunctionalInterface :Java 8 开始支持,标识一个匿名函数或函数式接口。
- @Repeatable :Java 8 开始支持,标识某注解可以在同一个声明上使用多次。
Annotation 的作用
编译检查 — @SuppressWarnings, @Deprecated 和 @Override
在反射中使用 Annotation — 在反射中解析并使用 Annotation。
/**
* Annotation在反射函数中的使用示例
*/
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
String[] value() default "unknown";
}
/**
* Person类。它会使用MyAnnotation注解。
*/
class Person {
/**
* sombody() 被 @MyAnnotation(value={"girl","boy"}) 所标注,
* @MyAnnotation(value={"girl","boy"}), 意味着MyAnnotation的value值是{"girl","boy"}
*/
@MyAnnotation(value={"girl","boy"})
public void somebody(String name, int age){
System.out.println("\nsomebody: "+name+", "+age);
}
}
public class AnnotationTest {
public static void main(String[] args) throws Exception {
// 新建Person
Person person = new Person();
// 获取Person的Class实例
Class<Person> c = Person.class;
// 获取 somebody() 方法的Method实例
Method mSomebody = c.getMethod("somebody", new Class[]{String.class, int.class});
// 执行该方法
mSomebody.invoke(person, new Object[]{"lily", 18});
// 判断 somebody() 方法是否包含MyAnnotation注解
if(method.isAnnotationPresent(MyAnnotation.class)){
// 获取该方法的MyAnnotation注解实例
MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
// 获取 myAnnotation的值,并打印出来
String[] values = myAnnotation.value();
for (String str:values)
System.out.printf(str+", ");
System.out.println();
}
}
}
根据 Annotation 生成帮助文档 — @Documented
能够帮忙查看代码,了解程序的大致结构。 — @Override, @Deprecated
参数校验(javax.validation.constraints)
- @NotEmpty:不能为null或者长度为0(String、Collection、Map的isEmpty()方法)接受CharSequence(String类)、Collection、Map、数组。
- @NotBlank:带注释的元素不能为null,并且必须至少包含一个。非空白字符(纯空格的String也是不符合规则的),接受CharSequence(String类)。
- @NotNull:必须不为 null,接受任何类型
- @Null:必须为 null,,接受任何类型
- @AssertTrue:必须为 true,接受布尔类型
- @AssertFalse:必须为 false,接受布尔类型
- @Pattern(regex=,flag=):必须符合指定的正则表达式(符合java规定的表达式),接受CharSequence(String类),{@code null}元素被认为是有效的
- @Email:有格式良好的电子邮件地址(电子邮件地址留给Bean验证提供者),构成有效数据的确切语义(必须是 Email 格式)。接受CharSequence(String类)
- @Min(value):必须是一个数值,其值必须大于等于指定的最小值,支持BigDecimal、BigInteger、byte、short、int、long,不支持double、float(由于舍入误差),{@code null}元素被认为是有效的
- @Max(value):必须是一个数值,其值必须小于或等于指定的最大值,支持BigDecimal、BigInteger、byte、short、int、long,不支持double、float(由于舍入误差),{@code null}元素被认为是有效的
- @DecimalMin(value):必须是一个数值,其值必须大于等于指定的最小值,支持BigDecimal、BigInteger、byte、short、int、long、CharSequence(String类),不支持double、float(由于舍入误差),{@code null}元素被认为是有效的
- @DecimalMax(value):必须是一个数值,其值必须小于等于指定的最大值,支持BigDecimal、BigInteger、byte、short、int、long、CharSequence(String类),不支持double、float(由于舍入误差),{@code null}元素被认为是有效的
- @Size(max=, min=):元素size/length必须在指定的范围内,支持CharSequence(String类)、Collection、Map、array,{@code null}元素被认为是有效的
- @Digits (integer, fraction):必须是一个数值,其值必须在可接受的范围内,支持BigDecimal、BigInteger、byte、short、int、long、CharSequence(String类),{@code null}元素被认为是有效的
- @Past:必须是一个过去的日期
- @PastOrPresent
- @Future 被注释的元素必须是一个将来的日期
- @FutureOrPresent
- @Negative
- @NegativeOrZero
- @Positive
- PositiveOrZero