赞
踩
本篇文章,将学习Java注解相关的知识点,包括
版本:Java注解是jdk1.5引入的技术
注解的定义,类似于Java中接口的定义,只需要在interface前面加上一个"@"。代码如下:
- public @interface Clazz_Annotation {
-
- }
上面就是定义了一个名字叫"Clazz_Annotation"的注解,注解和类,接口一样,都是Java的一种类型。那么,注解的花括号里都放些什么呢?不同于类和接口,注解里面只包含属性(也叫成员变量),不包含方法。
注解的属性的定义方式,也不同于类和接口。类里定义一个字段:String name,在注解里定义则是:String name()。
类里定义一个初始值为0的int型变量age是:int age = 0,在注解里则是int age() default 0。
代码如下:
- public @interface Clazz_Annotation {
- String name();
- int age() default 0;
- }
需要注意的是,如果注解的属性没有默认值,则在后续使用注解时需要指定该属性的值,否则编译失败。如果属性有默认的值,则可以不指定属性的值。
上面我们学习了如何定义注解,以及注解的属性,那么是不是可以使用注解了呢?莫急~,先了解一波元注解,然后再去使用注解效果更好。
那么,什么是元注解呢?元注解也是一个注解,它是用来给普通的注解进行注解,目的是给普通的注解添加限制条件。Java提供了五个元注解,分别是@Retention,@Target,@Inherited,@Documented,@Repeatable。
注意:由于元注解也是一种注解,因此会涉及对注解的使用,注解的使用会在后面详细介绍。
Retention:字面意思是保留期,用于确定普通注解的存活时间。
三种取值如下:
RetentionPolicy.SOURCE:普通注解在源码阶段保留,编译阶段被丢弃;
RetentionPolicy.CLASS :普通注解在编译阶段保留,在运行阶段被丢弃,不会加载进JVM;
RetentionPolicy.RUNTIME:普通注解在运行阶段也保留,它会加载进JVM;
值得一提的是,在使用反射提取注解的时候,由于反射是在运行阶段获取注解,因此该注解必须要添加元注解RetentionPolicy.RUNTIME,否则无法通过反射获取该注解。对注解的提取后面会讲到,这里了解一下即可。
栗子如下:
- @Retention(value = RetentionPolicy.RUNTIME)
- public @interface Clazz_Annotation {
- String name();
- int age() default 0;
- }
括号里value = RetentionPolicy.RUNTIME,就是给Retention注解的value属性设置值为RetentionPolicy.RUNTIME,这里涉及注解的使用,下面会具体讲到。
Target:字面意思是目标。当一个普通注解被@Target注解时,该普通注解就限定了使用范围。
有如下取值:
ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
ElementType.CONSTRUCTOR 可以给构造方法进行注解
ElementType.FIELD 可以给属性进行注解
ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
ElementType.METHOD 可以给方法进行注解
ElementType.PACKAGE 可以给一个包进行注解
ElementType.PARAMETER 可以给一个方法内的参数进行注解
ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
栗子如下:
- @Retention(value = RetentionPolicy.RUNTIME)
- @Target(value = {ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})
- public @interface Clazz_Annotation {
- String name();
- int age() default 0;
- }
注解Target的属性value是一个数组类型,给数组设置值,外面用{},多个值之间用逗号隔开。上面我们给它赋值了ElementType.METHOD, ElementType.TYPE, ElementType.FIELD,表示只可以在方法,类/接口/注解,字段上使用该注解。如果在构造方法上使用该注解,则编译错误。
Inherited:继承的意思。文字解释太啰嗦,直接看代码。
栗子如下:
- @Inherited
- @Retention(RetentionPolicy.RUNTIME)
- @interface Test {}
-
-
- @Test
- public class A {}
-
-
- public class B extends A {}
注解Test被@inherited注解,类A被Test注解,且类B继承了类A。如果类B没有被其他注解注解,那么类B也被Test注解。
Documented:文档的意思,作用是能够将注解中的元素包含到 Javadoc 中去。
栗子如下:
- @Retention(value = RetentionPolicy.RUNTIME)
- @Target(value = {ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})
- @Documented
- public @interface Clazz_Annotation {
- String name();
- int age() default 0;
- }
Documented注解没有属性,查看Documented的源码便知。
Repeatable:可重复的意思。被Repeatable注解的普通注解,可以多次使用。
版本:Java1.8引入的元注解。
栗子如下:
- public @interface ListAnnotation {
- ElementAnnotation[] lists();
- }
-
- @Repeatable(ListAnnotation.class)
- public @interface ElementAnnotation {
- String elment() default "";
- }
-
- @ElementAnnotation(elment = "a")
- @ElementAnnotation(elment = "b")
- @ElementAnnotation(elment = "c")
- public class Test {
-
- }
由代码可知:注解ElementAnnotation被@Repeatable注解,且Repeatable注解的属性值为ListAnnotation.class。ListAnnotation是一个注解,它是一个容器注解,用于存放注解ElementAnnotation。需要注意的是,注意注解ListAnnotation的属性为ElementAnnotation类型的数组。
然后使用注解ElementAnnotation给类Test添加注解,而且是3个ElementAnnotation注解,但是注解的属性值不同,分别为"a","b","c"。
那么如何使用注解呢?我们可以给类/接口/注解,一般方法,构造方法,字段等添加注解。
注意:如需限制某一注解使用的场景,使用元注解@Target即可,前面已经有所分析。
下面以给一个类Test添加注解为例,描述注解的使用。
首先定义一个注解Clazz_Annatation,代码如下:
- @Retention(value = RetentionPolicy.RUNTIME)
- @Target(value = {ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})
- public @interface Clazz_Annotation {
- String value() default "";
- }
定义一个类Test,使用注解Clazz_Annatation,代码如下:
- @Clazz_Annotation(value = "class_Test")
- public class Test {
- //...
- }
使用注解需要给属性赋值,是以括号内(属性名 = 值,属性名 = 值,...)形式呈现,多个属性的赋值以逗号隔开。本例中,由于value属性在注解中设置了默认值"",使用注解时可以不给value赋值。
如果注解只有一个value属性,可以简写为:@Clazz_Annotation("class_Test");
如果注解只有一个非value的属性,不可以简写,仍需要以(属性名 = 值)的格式;
如果注解没有属性,可以简写为:@Clazz_Annotation;
前面介绍了注解的使用,有木有发现注解并没有起到具体的作用,我们并不知道这样注解后有啥效果,于是涉及到注解的提取。注解的提取需要用Java的反射技术,在运行阶段获取注解。获取了注解,就获取了注解的属性相关的信息。
下面粘贴一个栗子,分别提取了类,字段,方法的注解,代码如下:
- @Clazz_Annotation("class_Test")
- public class Test {
-
- @Clazz_Annotation("field_obj")
- public Object obj = null;
-
- @Clazz_Annotation("method_main")
- public static void main(String[] args) {
- print();
- }
-
- private static void print() {
- //----class-----------
- boolean isExist = Test.class.isAnnotationPresent(Clazz_Annotation.class);
- if (isExist) {
- Clazz_Annotation clazz_Annotation = Test.class.getAnnotation(Clazz_Annotation.class);
- String value = clazz_Annotation.value();
- System.out.println("value = " + value);
-
- } else {
- System.out.println("Clazz_Annotation is not exist on class");
- }
-
- //------field-------
- try {
- Field field = Test.class.getField("obj");
- boolean isExist1 = field.isAnnotationPresent(Clazz_Annotation.class);
- if (isExist1) {
- Clazz_Annotation clazz_Annotation1 = field.getAnnotation(Clazz_Annotation.class);
- String value1 = clazz_Annotation1.value();
- System.out.println("value = " + value1);
- } else {
- System.out.println("Clazz_Annotation is not exist on field");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- //-----method----------
- try {
- Method method = Test.class.getMethod("main", String[].class);
- boolean isExist2 = method.isAnnotationPresent(Clazz_Annotation.class);
- if (isExist2) {
- Clazz_Annotation clazz_Annotation2 = method.getAnnotation(Clazz_Annotation.class);
- String value2 = clazz_Annotation2.value();
- System.out.println("value = " + value2);
- } else {
- System.out.println("Clazz_Annotation is not exist on method");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- }
- }

上述代码分别给类Test,字段obj,方法main添加了@Clazz_Annotation,且value属性的值分别为"class_Test","field_obj","method_main"。
调用反射类的isAnnotationPresent方法,判断类/字段/方法是否添加了Clazz_Annotation注解;
调用反射类的getAnnotation方法,返回一个Clazz_Annotation注解;
调用反射类的getAnnotations()方法,返回一个注解的数组Annotation[](如果有多个注解的话);
获取了注解Clazz_Annotation,再调用clazz_Annotation.value()使用注解的属性。
需要注意的是,要想通过反射成功提取注解,@Clazz_Annotation必须要使用元注解@Retention(RetentionPolicy.RUNTIME),否则程序运行后获取不到Annotation,也就是isAnnotationPresent方法返回null。
使用注解去修饰类,方法,字段,annotation不会自动生效,需要开发者提供相应的代码提取,并处理注解。提取并处理注解的代码,称之为APT,即Annotaton Processing Tool,注解解析工具。
注解的提取是利用的Java的反射机制,在运行阶段先获取注解,然后获取注解的属性。
在运行阶段使用反射技术,对性能会有一定的损耗,毕竟反射没有直接调用节约时间,这也是反射提取注解的缺点。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。