# 注解学习
JDK1.5 开始引入注解(Annotation),Java 中类、方法、变量、参数都可以被标注。
注解主要作用:
- 生成文档,通过代码中标识的注解生成 javadoc 文档。
- 编译检查,通过代码中标识的注解让编译器在编译期间进行检查。
- 编译时动态处理,编译时通过代码里标识的注解动态处理,例如动态生成代码。
- 运行时动态处理,运行时通过代码里标识的注解动态处理,例如使用反射注入实例。
关于三个名词:元注解(meta-annotation)、元数据(meta-data)、注解(annotation),我个人认为元注解是注解注解的注解,元数据是注解别称。
# JDK 内置的注解
# @Override
在 java.lang
包下,表示当前的方法定义将覆盖父类中的方法。
- 作用于方法
- 源码保留级别
@Target(ElementType.METHOD) | |
@Retention(RetentionPolicy.SOURCE) | |
public @interface Override { | |
} |
# @Deprecated
在 java.lang
包下,表示代码被弃用,如果使用了被 @Deprecated 注解的代码则编译器将发出警告。
- 生成文档
- 运行时保留级别
- 作用于构造器、属性、局部变量、方法、包、参数、类型
@Documented | |
@Retention(RetentionPolicy.RUNTIME) | |
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) | |
public @interface Deprecated { | |
} |
# @Suppvisewarnings
在 java.lang
包下,告诉编译器忽略指定的 警告信息。
- 可修饰包括类型、属性、方法、参数、构造器、局部变量
- 源码保留级别
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) | |
@Retention(RetentionPolicy.SOURCE) | |
public @interface SuppressWarnings { | |
String[] value(); | |
} |
示例:
/* | |
* 抑制单类型的警告: | |
*/ | |
@SuppressWarnings("unchecked") | |
public void addItems(String item) { | |
@SuppressWarnings("rawtypes") | |
List items = new ArrayList(); | |
…… | |
} | |
/* | |
* 抑制多类型的警告: | |
*/ | |
@SuppressWarnings(value = {"unchecked", "rawtypes"}) | |
public void addItems(String item) { | |
…… | |
} |
具体警告类型查表:
参数 | 作用 | 原描述 |
---|---|---|
all | 抑制所有警告 | to suppress all warnings |
boxing | 抑制装箱、拆箱操作时候的警告 | to suppress warnings relative to boxing/unboxing operations |
cast | 抑制映射相关的警告 | to suppress warnings relative to cast operations |
dep-ann | 抑制启用注释的警告 | to suppress warnings relative to deprecated annotation |
deprecation | 抑制过期方法警告 | to suppress warnings relative to deprecation |
fallthrough | 抑制确在 switch 中缺失 breaks 的警告 | to suppress warnings relative to missing breaks in switch statements |
finally | 抑制 finally 模块没有返回的警告 | to suppress warnings relative to finally block that don’t return |
hiding | 抑制与隐藏变数的区域变数相关的警告 | to suppress warnings relative to locals that hide variable() |
incomplete-switch | 忽略没有完整的 switch 语句 | to suppress warnings relative to missing entries in a switch statement (enum case) |
nls | 忽略非 nls 格式的字符 | to suppress warnings relative to non-nls string literals |
null | 忽略对 null 的操作 | to suppress warnings relative to null analysis |
rawtype | 使用 generics 时忽略没有指定相应的类型 | to suppress warnings relative to un-specific types when using |
restriction | 抑制与使用不建议或禁止参照相关的警告 | to suppress warnings relative to usage of discouraged or |
serial | 忽略在 serializable 类中没有声明 serialVersionUID 变量 | to suppress warnings relative to missing serialVersionUID field for a serializable class |
static-access | 抑制不正确的静态访问方式警告 | to suppress warnings relative to incorrect static access |
synthetic-access | 抑制子类没有按最优方法访问内部类的警告 | to suppress warnings relative to unoptimized access from inner classes |
unchecked | 抑制没有进行类型检查操作的警告 | to suppress warnings relative to unchecked operations |
unqualified-field-access | 抑制没有权限访问的域的警告 | to suppress warnings relative to field access unqualified |
unused | 抑制没被使用过的代码的警告 | to suppress warnings relative to unused code |
# 元注解
元注解就是注解注解的注解,一般用于修饰注解
除了JDK定义好的注解,我们还可以自定义注解,JDK1.5提供了四个标准用来对注解类型进行注解的注解类
@Target
@Retention
@Document
@Inherited
# @Target 注解
描述注解的使用范围
Target 注解用来说明被注解的注解类可修饰的对象的范围:
注解可以修饰packages types(类、接口、枚举、注解类)、类成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量
取值范围在 ElementType 枚举中:
public enum ElementType { | |
TYPE, // 类、接口、枚举类 | |
FIELD, // 成员变量(包括:枚举常量) | |
METHOD, // 成员方法 | |
PARAMETER, // 方法参数 | |
CONSTRUCTOR, // 构造方法 | |
LOCAL_VARIABLE, // 局部变量 | |
ANNOTATION_TYPE, // 注解类 | |
PACKAGE, // 可用于修饰:包 | |
TYPE_PARAMETER, // 类型参数,JDK 1.8 新增 | |
TYPE_USE // 使用类型的任何地方,JDK 1.8 新增 | |
} |
# @Retention 注解
描述注解保留的时间范围
限定所注解的注解类注解到其他类上后,可以被保留到何时,一共三个策略,定义在RetentionPolicy枚举中:
public enum RetentionPolicy { | |
SOURCE, // 源文件保留 | |
CLASS, // 编译期保留,默认值 | |
RUNTIME // 运行期保留,可通过反射去获取注解信息 | |
} |
# @Documented 注解
描述在使用 javadoc 工具为类生成帮助文档时是否需要保留注解信息
# @Inherited 注解
使被修饰的注解具有继承性
# 注解与反射
我们知道 spring 中有很多注解很实用,如使用注解驱动 ioc 依赖注入,好的注解可以简化代码提高效率。
# AnnotationElement
在 java.lang.reflect
包下有 AnnotationElement
接口,标识一个被注解的 Java 语言元素(Class、Method、Field、Constructor、GenericDeclaration 等对应的实现都实现了该接口),用于获取注解的内容。** 注意:只有注解作用范围定义为 RUNTIME 时,该注解才运行时可见,这样 class 文件被装载时其中的注解才能被虚拟机读取。 **
方法使用:
isAnnotationPresent(Class<? extends Annotation>)
判断元素上是否包含指定类型的注解,包含则返回 true,反之 false
getAnnotation(Class<T>)
返回该元素上的注解,不存在返回 null
getAnnotations()
返回该元素存在的所有注解(数组形式),无注解返回长度 0 的数组
getAnnotationsByType(Class<T>)
返回指定类型的注解数组
另外加
Declared
的方法跟不加的差不多,只不过多了一个忽略继承的注解
# 自定义注解
注解 @Extra
@Documented | |
@Retention(RetentionPolicy.RUNTIME) | |
@Target({ElementType.METHOD,ElementType.FIELD}) | |
@Inherited | |
public @interface Extra { | |
enum Type { | |
/** | |
* 类 | |
*/ | |
CLASS, | |
/** | |
* 方法 | |
*/ | |
METHOD | |
} | |
// 声明枚举 | |
Type type() default Type.CLASS; | |
String value() default "默认"; | |
} |
使用注解的类 TestAnnotation
public class TestAnnotation { | |
@Extra("覆盖默认A") | |
public void hasAnnoA(){ | |
System.out.println("方法"); | |
} | |
@Extra("覆盖默认B") | |
public void hasAnnoB(){ | |
System.out.println("方法"); | |
} | |
public void noAnno(){ | |
System.out.println("方法"); | |
} | |
} |
主类 Main
public class Main { | |
public static void main(String[] args) { | |
// 获取 TestAnnotation 所有方法 | |
Method[] methods = TestAnnotation.class.getMethods(); | |
//Lambda 取 methods 中注解了 @Extra 的方法的注解值 | |
Stream.of(methods).filter(method -> method.isAnnotationPresent(Extra.class)) | |
.forEach(method -> System.out.println(method.getAnnotation(Extra.class).value())); | |
} | |
// 上面 Lambda 等同于下面 foreach | |
for(Method method: methods){ | |
if(method.isAnnotationPresent(Extra.class)){ | |
System.out.println(method.getDeclaredAnnotation(Extra.class).value()); | |
} | |
} | |
} |
显示:
覆盖默认A | |
覆盖默认B |