MOOC 个人学习笔记
# 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 类型
- 注解类型
- 由前面类型组成的数组
public @interface BugReport { | |
enum Status {UNCONFIRMED, CONFIRMED, FIXED, NOTABUG}; | |
boolean showStopper() default true; | |
String assiganedTo() default "[none]"; | |
Status status() default Status.UNCONFIRMED; | |
String[] reportedBy(); | |
} |
# 注解使用
- @Test
public @interface Test { | |
} |
- @SingleTest
- @SingleTest(5)
- @SingleTest(value=5)
public @interface SingleTest { | |
int value() default 0; | |
} |
- @MultipleTest
- @MultipleTest(a=1)
- @MultipleTest(a=1,b=2)
- @MultipleTest(b=2,a=1)
- @MultipleTest (1,2) 是错误写法
public @interface MultipleTest { | |
int a() default 0; | |
int b() default 0; | |
} |
# 注解使用的位置
- @Target 可以限定位置
@Retention(RetentionPolicy.RUNTIME) | |
// 表示该注解会保留在 class 文件中 | |
@Target(ElementType.METHOD) | |
// 表示该注解只能用于方法 |
- 允许的位置
- 包
- 类
- 接口
- 方法
- 构造器
- 成员变量
- 局部变量 / 形参变量 / 类型参数
# 注解作为单元测试
public class Foo { | |
@SingleTest(1) | |
public static void m1(int a) { | |
if(a<0) | |
{ | |
throw new RuntimeException("Crash"); | |
} | |
} | |
public static void m2() { | |
} | |
@SingleTest(value=-2) | |
public static void m3(int a) { | |
if(a<0) | |
{ | |
throw new RuntimeException("Crash"); | |
} | |
} | |
} | |
public static void main(String[] args) throws Exception { | |
int passed = 0, failed = 0; | |
String className = "annotations.single.Foo"; | |
for (Method m : Class.forName(className).getMethods()) { | |
if (m.isAnnotationPresent(SingleTest.class)) { | |
System.out.println(m.getName()); | |
SingleTest st = m.getAnnotation(SingleTest.class); | |
try { | |
m.invoke(null,st.value()); | |
passed++; | |
} catch (Throwable ex) { | |
System.out.printf("Test %s failed: %s %n", m, ex.getCause()); | |
failed++; | |
} | |
} | |
} | |
System.out.printf("Passed: %d, Failed %d%n", passed, failed); | |
} |
# 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 引入
- 表示被修饰的注解可以重复应用标注
- 需要定义注解和容器注解
// 普通注解 | |
@Retention(RetentionPolicy.RUNTIME) | |
@Repeatable(RepeatableAnnotations.class) | |
public @interface RepeatableAnnotation { | |
int a() default 0; | |
int b() default 0; | |
int c() default 0; | |
} | |
// 容器注解 | |
@Retention(RetentionPolicy.RUNTIME) | |
public @interface RepeatableAnnotations { | |
RepeatableAnnotation[] value(); | |
} |
# 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 中,访问速度快