本博文是对Java中注解相关知识点的简单总结,如有叙述不清晰或是不许确的地方,但愿你们能够指正,谢谢你们:)java
咱们你们都知道Java代码中使用注释是为了向之后阅读这份代码的人解释说明一些事情,注解是注释的升级版,它能够向编译器、虚拟机等解释说明一些事情。好比咱们很是熟悉的@Override就是一种元注解,它的做用是告诉编译器它所注解的方法是重写父类的方法,这样编译器就会去检查父类是否存在这个方法,以及这个方法的签名与父类是否相同。git
也就是说,注解是描述Java代码的代码,它可以被编译器解析,注解处理工具在运行时也可以解析注解。咱们在Java源文件中使用注释,是为了之后咱们或他人再来读这段代码时,可以更好地理解它。Javadoc工具能够解析咱们在源代码中为类、方法、变量等添加的描述信息,并根据这些描述信息自动生成一个HTML文档,这些自动生成的文档便可做为API帮助文档。只要咱们为类、方法等添加的描述信息符合Javadoc要求的语法,咱们就可以使用Javadoc工具根据咱们的描述信息自动生成一个帮助文档。而注解比java注释和Javadoc要强大得多,它们三者之间的重大的区别在于,Java注释和Javadoc描述所发挥的做用仅仅到编译时就止步了,而注解直到运行时都可以发挥做用。数组
咱们知道,使用“transient”关键字能够告诉编译器这个域不可序列化。相比于用”transient“这样的关键字修饰一个属性,注解为咱们提供了为类/方法/属性/变量添加描述信息的更通用的方式,而这些描述信息对于开发者、自动化工具、Java编译器和Java运行时来讲都是有意义的,也就是说他们都能“读懂”注解信息。”transient“关键字是一个修饰符,而注解也是一种修饰符。除了传递信息,咱们也可使用注解生成代码。咱们可使用注解,而后让注解解析工具来解析它们,以此来生成一些”模板化“的代码。好比Hibernate、Spring、Axis这些框架大量使用了注解,来避免一些重复的工做。框架
元注解即用来描述注解的注解,好比如下代码中咱们使用“@Target”元注解来讲明MethodInfo这个注解只能应用于对方法进行注解:ide
@Target(ElementType.METHOD)
public @interface MethodInfo { ... }
下面咱们来具体介绍一下几种元注解。函数
当一个注解类型被@Documented元注解所描述时,那么不管在哪里使用这个注解,都会被Javadoc工具文档化。咱们来看一下它的定义:工具
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Documented { }
咱们从以上代码中能够看到,定义注解使用@interface关键字,这就比如咱们定义类时使用class关键字,定义接口时使用interface关键字同样,注解也是一种类型。这个元注解被@Documented修饰,表示它自己也会被文档化。@Retention元注解的值RetentionPolicy.RUNTIME表示@Documented这个注解能保留到运行时;@Target元注解的值ElementType.ANNOTATION_TYPE表示@Documented这个注解只可以用来描述注解类型。spa
代表被修饰的注解类型是自动继承的。具体解释以下:若一个注解类型被Inherited元注解所修饰,则当用户在一个类声明中查询该注解类型时,若发现这个类声明中不包含这个注解类型,则会自动在这个类的父类中查询相应的注解类型,这个过程会被重复,直到该注解类型被找到或是查找完了Object类还未找到。这个元注解的定义以下:code
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Inherited { }
咱们能够看到这个元注解类型被@Documented所注解,可以保留到运行时,只能用来描述注解类型。blog
咱们在上面已经见到个这个元注解,它表示一个注解类型会被保留到何时,好比如下代码表示Developer注解会被保留到运行时:
@Retention(RetentionPolicy.RUNTIME)
public @interface Developer { String value(); }
@Retention元注解的定义以下:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); }
咱们在使用@Retention时,后面括号里的内容即表示他的取值,从以上定义咱们能够看到,取值的类型为RetentionPolicy,这是一个枚举类型,它能够取如下值:
这个元注解说明了被修饰的注解的应用范围,也就是被修饰的注解能够用来注解哪些程序元素,它的定义以下:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
从以上定义咱们能够看到它也会保留到运行时,并且它的取值是为ElementType[]类型(一个数组,意思是能够指定多个值),ElementType是一个枚举类型,它能够取如下值:
Java自己内建了一些注解,下面咱们来介绍一下咱们在平常开发中比较常见的注解:@Override、@Deprecated、@SuppressWarnings。相信咱们你们或多或少都使用过这三个注解,下面咱们一块儿再从新认识一下它们。
咱们先来看一下这个注解类型的定义:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
从它的定义咱们能够看到,这个注解能够被用来修饰方法,而且它只在编译时有效,在编译后的class文件中便再也不存在。这个注解的做用咱们你们都不陌生,那就是告诉编译器被修饰的方法是重写的父类的中的相同签名的方法,编译器会对此作出检查,若发现父类中不存在这个方法或是存在的方法签名不一样,则会报错。
这个注解的定义以下:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE}) public @interface Deprecated { }
从它的定义咱们能够知道,它会被文档化,可以保留到运行时,可以修饰构造方法、属性、局部变量、方法、包、参数、类型。这个注解的做用是告诉编译器被修饰的程序元素已被“废弃”,再也不建议用户使用。
这个注解咱们也比较经常使用到,先来看下它的定义:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
它可以修饰的程序元素包括类型、属性、方法、参数、构造器、局部变量,只能存活在源码时,取值为String[]。它的做用是告诉编译器忽略指定的警告信息,它能够取的值以下所示:
这个注解的使用示例以下:
@SuppressWarning(value={"deprecation", "unchecked"}) public void myMethos() {...}
经过使用以上注解,咱们告诉编译器忽略myMethod方法中因为使用了废弃的类或方法或是作了未检查的转换而产生的警告。
咱们能够建立咱们本身的注解类型并使用它。请看下面的示例:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Inherited public @interface MethodInfo { String author() default "absfree"; String date(); int version() default 1; }
在自定义注解时,有如下几点须要咱们了解:
咱们再把上面提到过的@SuppressWarnings这个注解类型的定义拿出来看一下,这个注解类型是系统为咱们定义好的,它的定义以下:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.SOURCE) public @interface SuppressWarnings { String[] value(); }
咱们能够看到,它只定义了一个注解方法value(),它的返回值类型为String[],没有指定默认返回值。咱们使用@SuppressWarnings这个注解所用的语法以下:
@SuppressWarnings(value={"value1", "value2", ...})
也就是在注解类型名称后的括号内为每一个注解方法指定返回值就可使用这个注解。下面咱们来看看怎么使用咱们自定义的注解类型@MethodInfo:
public class AnnotationTest { @MethodInfo(author="absfree", date="20160410") public static void main(String[] args) { System.out.println("Using custom annotation..."); } }
那么如今问题来了,咱们使用的自定义注解对于编译器或是虚拟机来讲是有意义的吗(编译器或是虚拟机能读懂吗)?显然咱们什么都不作的话,编译器或者虚拟机是读不懂咱们的自定义注解的。下面咱们来介绍如下注解的解析,让编译器或虚拟机可以读懂咱们的自定义注解。
编译时注解指的是@Retention的值为CLASS的注解,对于这类注解的解析,咱们只需作如下两件事:
实际上,编译器在编译时会自动查找全部继承自 AbstractProcessor 的类,而后调用他们的 process 方法。所以咱们只要作好上面两件事,编译器就会主动去解析咱们的编译时注解。如今,咱们把上面定义的MethodInfo的Retention改成CLASS,咱们就能够按照如下代码来解析它:
@SupportedAnnotationTypes({ "com.custom.customannotation.MethodInfo" }) public class MethodInfoProcessor extends AbstractProcessor { @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) { HashMap<String, String> map = new HashMap<String, String>(); for (TypeElement typeElement : annotations) { for (Element element : env.getElementsAnnotatedWith(typeElement)) { MethodInfo methodInfo = element.getAnnotation(MethodInfo.class); map.put(element.getEnclosingElement().toString(), methodInfo.author()); } } return false; } }
@SupportedAnnotationTypes注解描述了Processor要解析的注解的名字。process 函数的annotations参数表示 表示待处理的注解集,env表示当前或是以前的运行环境。process函数的返回值表示annotations中的注解是否被这个Processor接受。
首先咱们把MethodInfo注解类型中Retention的值改回原来的RUNTIME,接下来咱们介绍如何经过反射机制在运行时解析咱们的自定义注解类型。
java.lang.reflect包中有一个AnnotatedElement接口,这个接口定义了用于获取注解信息的几个方法:
T getAnnotation(Class annotationClass) //返回该程序元素的指定类型的注解,若不存在这个类型的注解则返回null Annotation[] getAnnotations() //返回修饰该程序元素的全部注解 Annotation[] getDeclaredAnnotations() //返回直接修饰该元素的全部注解 boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) //当该程序元素被指定类型注解修饰时,返回true,不然返回false
解析咱们上面的自定义注解MethodInfo的相关示例代码以下(AnnotationParser.java):
public class AnnotationParser { public static void main(String[] args) { try { Class cls = AnnotationTest.class; for (Method method : cls.getMethods()) { MethodInfo methodInfo = method.getAnnotation(MethodInfo.class); if (methodInfo != null) { System.out.println("method name:" + method.getName()); System.out.println("method author:" + methodInfo.author()); System.out.println("method date:" + methodInfo.date()); System.out.println("method version:" + methodInfo.version()); } } } catch (Exception e) { e.printStackTrace(); } } }
运行以上代码咱们能够获得如下输出:

这说明咱们已经成功解析了自定义注解。关于注解有点咱们须要明确的是,做为描述代码自己的一种元数据,注解是一种”被动“的信息。也就是说,必须由编译器或虚拟机来“主动”解析它,它才能发挥本身的做用。
1. Java Documention
2. 公共技术点之Java注解
3. Java 注解