1.什么是注解
从JDK5开始,Java增加了注解,注解是代理里特殊的标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。通过使用注解,开发人员可以在不改变原有逻辑的情况下,在源文件中嵌入一些补充信息。代码分析工具,开发工具和部署工具都可以通过这些补充信息进行验证,处理或者进行部署。
JavaSE5内置了三种注解,定义在java.lang包中:
- @Override : 表示当前方法覆盖超类中的方法。如果你所写的方法和超类中的方法不同的话,编译器会报错。主要用于检查。
- @Deprecated : 表明当前的元素已经不适用。当使用了注解为
@Deprecated
的元素时,编译器会报出警告。 - @SuppressWarnings : 关闭不当的编译器警告。
2.注解的分类
按照注解的运行机制,可以将注解分为三类。
- 源码注解 只在源码存在,编译成class文件就不存在了
- 编译时注解 源码和编译后的class文件都存在,如JDK自带的三种注解@Override,@Deprecated,@SuppressWarnings
- 运行时注解 在程序运行时可以起作用
3.元注解
元注解是用来标注其它注解来创建新注解的,时注解的注解。元注解的类型有以下几种:
- @Target:注解所修饰的对象范围
- @Inherited:表示注解可以被继承
- @Documented:表示这个注解应该被JavaDoc工具记录
- @Rentation:用来声明注解的保留策略
- @Repeable:JDK8新增,允许一个注解在同一声明类型(类,属性或方法)上多次使用。
@Target注解的取值是一个ElementType类型的枚举,其中有以下几种取值,对应不同的对象范围。
- ElementType.TYPE:能修饰类,接口或者枚举类型。
- ElementType.FIELD:能修饰成员变量
- ElementType. METHOD:能修饰方法
- ElementType. PARAMETER:能修饰参数
- ElementType. CONSTRUCTOR:能修饰构造方法
- ElementType.LOCAL_VARIABLE:能修饰局部变量
- ElementType. ANNOTATION_TYPE:能修饰注解
- ElementType. PACKAGE:能修饰包
- ElementType.TYPE_PARAMETER:能修饰参数声明
- ElementType.TYPE_USE:使用类型
@Retention注解有3种类型,分别表示不同级别的保留策略。
- RetentionPolicy.SOURCE:源码级注解,注解信息只会保留在Java源码中,源码在编译后,注解信息将会被丢弃,不会保留在.class文件中。
- RetentionPolicy.CLASS:编译时注解。注解信息会保留在.java源码以及.class中。当运行java程序时,JVM会丢弃该注解信息,不会保留在JVM中,许多注解类框架,如EventBus,ButterKnife他们都是使用编译时注解信息开发的,针对编译时注解我们采用AbstractProcessor来处理注解信息。
- RetentionPolicy.RUNTIME:运行注解。当运行Java程序时,JVM也会保留该注解信息,可以通过反射获取该注解信息。
@Documented 被修饰的注解会生成到javadoc中
@Inherited 可以让注解类似被继承一样,但是这并不是真的继承。通过使@Inherited,只可以让子类类对象使用getAnnotations()反射获取父类被@Inherited修饰的注解。
4.自定义注解实例
创建一个注解类JInfo,注解目标类型为METHOD,保留机制设置为运行时注解。该注解有一个注解成员info,为String类型,默认值为空字符串。
@Target(METHOD) //注解目标类型为方法(METHOD)
@Retention(RetentionPolicy.RUNTIME) //留机制设:运行时注解
public @interface JInfo {
public String info() default ""; ///注解成员 类型为String,默认为空字符串
}
就这么简单,一个注解就建好了。注解本身不会起到任何作用,需要配合注解处理器才能发挥一定的作用。下面展示一下在代码中是如何去添加注解并访问这些注解的,此处需要说明的是,如果需要在代码中去访问注解内容,注解保留机制需设置为运行时(RetentionPolicy.RUNTIME
),否则无法访问。
public class Test {
public static void main(String[] args) {
Method[] methods = Test.class.getMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(JInfo.class)) {
JInfo jInfo = method.getAnnotation(JInfo.class);
System.out.println(jInfo.info());
}
}
}
@JInfo (info = "this is info string")
public void test() {
System.out.println("test");
}
}
输出结果即注解内容:
this is info string
我们在进行注解时,如果注解是有成员的,那么就必须在注解后的括号里为每个成员指定数据,或者在注解类中为成员指定默认值,注解中不允许null值。
5.编译时注解
运行时注解一般在代码中需要通过反射去获取和使用,因此效率较低。为了解决效率问题,我们可以使用编译时注解,在编译期将注解转换为代码,从而提高执行效率。Annotation Processor是javac的一个工具,它用来在编译时扫描和处理注解。通过Annotation Processor可以获取到注解和被注解对象的相关信息,然后根据注解自动生成Java代码,省去了手动编写,提高了编码效率。