文章目录
一、注解常识
1.1 注解是什么?
注解(Annotation。。注意:Annotation本身在JDK中就是一个Interface ),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
简单来说注解其实就是代码中的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相对应的处理。不过呢,假如没有对这些特殊标记做处理,那注解其实没啥用。
1.2 注解的属性
注解只有成员变量,没有方法
注解不是代码本身的一部分,但是 根据Target说明, 某些注解可以在程序运行的时候接受代码的提取
1.3 JDK内置了哪些注解?
-
Overried
Overried是告诉编译器要检查该方法是实现父类的方法。 -
Deprecated用于标记一些过时的代码。
-
SuppressWarnings
SuppressWarnings用于消除一些警告信息,使用集合的时候,如果没有指定泛型,IDE会提示安全检查的警告。 -
FunctionalInterface
FunctionalInterface是JDK8中的注解,用来指定该接口是函数式接口。 -
SafeVarargs
SafeVarargs是JDK 7中的注解,主要目的是处理可变长参数中的泛型,此注解告诉编译器:在可变长参数中的泛型是类型安全的。
泛型的类型安全是怎么回事?
1.4 怎么自定义一个注解?
在Java中,类使用class定义,接口使用interface定义,注解和接口的定义差不多,增加了一个@符号,即@interface,代码如下:
public @interface EnableAuth {
}
注解中可以定义元素,用于信息的描述,跟接口中方法的定义类似,代码如下:
public @interface EnableAuth {
String name();
}
还可以添加默认值:
public @interface EnableAuth {
String name() default "enable auth ";
}
可见:注解的元素看起来就像接口的方法,不过你可以为注解的元素设定默认值,JDK8以后,接口也可以定义默认方法了。
上面的介绍只是完成了自定义注解的第一步,开发中日常使用注解大部分是用在类上,方法上,字段上,示列代码如下:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EnableAuth {
}
- Target
用于指定被修饰的注解修饰哪些程序单元,也就是上面说的类,方法,字段 - Retention
用于指定被修饰的注解被保留多长时间,分别SOURCE(注解仅存在于源码中,在class字节码文件中不包含),CLASS(默认的保留策略,注解会在class字节码文件中存在,但运行时无法获取),RUNTIME(注解会在class字节码文件中存在,在运行时可以通过反射获取到)三种类型,如果想要在程序运行过程中通过反射来获取注解的信息需要将Retention设置为RUNTIME - Documented
用于指定被修饰的注解类将被javadoc工具提取成文档 - Inherited
用于指定被修饰的注解类将具有继承性
1.5 注解元素的可用类型
注解时的定义的元素类型不是随意选用的,而是限于下面五种:
- 所有基本类型(即使是包装类,也不可以!!)
- String
- enum
- Annotation
- class
1.6 注解元素的默认值限制
编译器对注解元素的默认值是有严格限制的,要么给出默认值,要么在使用注解时提供元素值,不能存在不确定的值。
- 默认值不能为null,而且在代码中设定的时候也不能为null,因此,注解元素不能直接表示一个元素的存在、缺失,但是可以用 -1 、空串等表示“不存在”
二、注解案例
demo:定义一个注解,可以用来追踪项目中的用例。若一个方法或一组方法已经实现了某实例的需求,则加上该注解。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
int id();
String desc() default "no desc";
}
public class PasswordUtils {
@UseCase(id = 1,desc = "at least have one numeric")
public boolean validate(String password) {
return password.matches("\\w*\\d\\w*");
}
@UseCase(id = 2)
public String encrypt(String password) {
return new StringBuilder(password).reverse().toString();
}
}
上面定义了注解,并且在方法上加上了注解,但这其实还没有卵用,因为没有对应的注解处理器来处理:
public class UseCaseTracker {
public static void trackUseCases(List<Integer> useCaseIds, Class<?> clazz) {
for (Method e : clazz.getDeclaredMethods()) {
UseCase useCase = e.getAnnotation(UseCase.class);
if (useCase != null) {
System.out.println(" found use case " + useCase.id() + useCase.desc());
useCaseIds.remove(useCase.id());
}
}
useCaseIds.forEach(e -> System.out.println(" missing " + e));
}
public static void main(String[] args) {
ArrayList<Integer> useCases = Lists.newArrayList(1, 2, 3, 4);
trackUseCases(useCases, PasswordUtils.class);
}
}
这个Demo里用到了两个比较重要的API:
- getAnnotation()
- getDeclaredMethods()
三、apt技术
APT技术,从Java6开始到Java7,已经确认过时了。