1.注解基础
- 从JDK 1.5 引入
- 位于源码中(代码/注释/注解),使用其他工具进行处理的标签
- 注解用来修饰程序的元素,但不会对被修饰的对象有直接的影响
- 只有通过某种配套的工具才会对注解信息进行访问和处理
- 主要用途
- 提供信息给编译器/IDE工具
- 可用于其他工具来产生额外的代码/配置文件等
- 有一些注解可在程序运行时访问,增加程序的动态性
JDK预定义的普通注解(部分)
- @Override 表示继承和改写 自带注解
- @Deprecated 表示废弃 自带注解
- @SuppressWarnings 表示压制警告 自带注解
- @SafeVarargs 不会对不定项参数做危险操作 自带注解
- @FunctionInterface 声明功能性接口 自带注解
JDK预定义的元注解(部分)
- @Target 设置目标范围 元注解
- @Retention 设置保持性 元注解
- @Documented 文档 元注解
- @Inherited 注解继承 元注解
- @Repeatable 此注解可以重复修饰 元注解
2.预定义的普通注解
@Override
- 修饰方法,检查该方法是父类的方法
- 强制该函数代码必须符合父类中该方法的定义
- 避免代码错误
@Deprecated
- 修饰类/类的元素/包
- 标注为废除,建议程序员不再使用这个类/元素/包
@SuppressWarnings
- 可以修饰变量/方法/构造函数/类等
- 压制各种不同类型的警告信息,使得编译器不显示警告
- 各种不同类型是叠加,如修饰类的警告类型,和修饰方法的警告类型,对于方法来说,是叠加的
- 警告类型名称是编译器/IDE工具自己定的,Java规范没有强制要求哪些名称。编译器厂商需要自行协商,保证同名警告类型在各个编译器上一样工作
- @SuppressWarnings
- @SuppressWarnings(“unchecked”) 忽略unchecked警告信息
- @SuppressWarnings(“deprecated”) 忽略过时方法的警告信息
- @SuppressWarnings({“unchecked”,“deprecated”}) 忽略两种警告信息
- @ SuppressWarnings(values={“unchecked”,“deprecated”}) 同上
- @ SuppressWarnings( “all”) 忽略所有的警告信息
- JLS 只规定了deprecated和unchecked两种
- 其他的警告类型
- all,忽略所有的警告
- cast,忽略类转型警告
- serial,忽略实现Serializable接口的,没有定义serialVersionUID
- 使用javac -X 可以看当前的编译器使用哪些警告类型
3.自定义注解
- 注解定义:扩展java.lang.annotation.Annotation注解接口
- 注解可以包括的类型
- 8种基本类型(int/short/long/float/double/byte/char/boolean) –String
- Class
- enum类型
- 注解类型
- 由前面类型组成的数组
1 | public BugReport { |
注解使用
- @Test
1 | public Test { |
- @SingleTest
- @SingleTest(5)
- @SingleTest(value=5)
1 | public SingleTest { |
- @MultipleTest
- @MultipleTest(a=1)
- @MultipleTest(a=1,b=2)
- @MultipleTest(b=2,a=1)
- @MultipleTest(1,2)是错误写法
1 | public MultipleTest { |
注解使用的位置
- @Target可以限定位置
1 |
|
- 允许的位置
- 包
- 类
- 接口
- 方法
- 构造器
- 成员变量
- 局部变量/形参变量/类型参数
注解作为单元测试
1 | public class Foo { |
4.预定义的元注解
Retention
- 示例@Retention(RetentionPolicy.RUNTIME)
- 这个注解用来修饰其他注解的存在范围
- RetentionPolicy.SOURCE 注解仅存在源码,不在class文件
- RetentionPolicy.CLASS 默认的注解保留策略 注解存在于.class文件,但是不能被JVM加载
- RetentionPolicy.RUNTIME 这种策略下,注解可以被JVM运行时访问到。通常情况下,可以结合反射来做一些事情
Target
- 限定目标注解作用于什么位置@Target({ElementType.METHOD})
- ElementType.ANNOTATION_TYPE(注:修饰注解)
- ElementType.CONSTRUCTOR
- ElementType.FIELD
- ElementType.LOCAL_VARIABLE
- ElementType.METHOD
- ElementType.PACKAGE
- ElementType.PARAMETER
- ElementType.TYPE(注:任何类型,即上面的的类型都可以修饰)
Inherited
- 让一个类和它的子类都包含某个注解
- 普通的注解没有继承功能
Repeatable
- 自JDK1.8引入
- 表示被修饰的注解可以重复应用标注
- 需要定义注解和容器注解
1 | //普通注解 |
Documented
- 指明这个注解可以被Javadoc工具解析,形成帮助文档
5.注解的解析
RetentionPolicy.RUNTIME
- 注解在class文件中,被JVM加载,可用反射解析注解
- Class.getAnnotations()
- Class.isAnnotation()
- Class. .isAnnotationPresent(Class annotationClass)
- Method.getAnnotations()
- Method.isAnnotationPresent(Class annotationClass)
- Field.getAnnotations()
- Field.isAnnotationPresent(Class annotationClass)
- Constructor.getAnnotations()
- Constructor.isAnnotationPresent(Class annotationClass)
RetentionPolicy.CLASS
- 注解在class文件中,但JVM没有加载
- 只能采用字节码工具进行特殊处理
- 如ASM工具,https://asm.ow2.io/
RetentionPolicy.SOURCE
- 注解在java文件中,不在class文件中,也不会被JVM加载
- 只有在源码级别进行注解处理
- Java提供注解处理器来解析带注解的源码,产生新的文件
- 注解处理器继承AbstractProcessor,重写process方法
- javac –processor Processor1, Processor2, … sourceJavaFile
- 编译器定位源文件的注解,然后依次启动注解处理器执行处理。如果某个注解处理器产生新的源文件,那么将重复执行这个处理过程。
- 注解处理器只能产生新文件,不会修改已有的源文件
Java 5/6提供的APT工具
- 一个可以处理注解的命令行工具
- 包含在com.sun.mirror包中
- 于Java 7被标记为Deprecated,计划于将来版本中废除
6.RUNTIME注解的实现本质
- 注解采用接口中的方法来表示变量
- Java为注解产生一个代理类。这个代理类包括一个AnnotationInvocationHandler成员变量
- AnnotationInvocationHandler有一个Map的成员变量,用来存储所有的注解的属性赋值
- 在程序中,调用注解接口的方法,将会被代理类接管,然后根据方法名字,到Map里面拿相应的Value并返回
- 传统的接口中的变量,都是public final static
- 注解需要随意赋值
- 注解方法表示变量
- 采用代理类拦截注解方法访问
- 所有的注解的赋值,都放在Map中,访问速度快