当前位置:   article > 正文

点亮技能之Java注解_default=0

default=0

 

一,写在前面

 本篇文章,将学习Java注解相关的知识点,包括

  • 注解的定义
  • 注解的属性
  • 元注解
  • 如何使用注解
  • 注解的提取

版本:Java注解是jdk1.5引入的技术

 

二,注解的定义

注解的定义,类似于Java中接口的定义,只需要在interface前面加上一个"@"。代码如下:

  1. public @interface Clazz_Annotation {
  2. }

上面就是定义了一个名字叫"Clazz_Annotation"的注解,注解和类,接口一样,都是Java的一种类型。那么,注解的花括号里都放些什么呢?不同于类和接口,注解里面只包含属性(也叫成员变量),不包含方法。

 

三,注解的属性

注解的属性的定义方式,也不同于类和接口。类里定义一个字段:String name,在注解里定义则是:String name()。

类里定义一个初始值为0的int型变量age是:int age = 0,在注解里则是int age() default 0。

代码如下:

  1. public @interface Clazz_Annotation {
  2. String name();
  3. int age() default 0;
  4. }

需要注意的是,如果注解的属性没有默认值,则在后续使用注解时需要指定该属性的值,否则编译失败。如果属性有默认的值,则可以不指定属性的值。

上面我们学习了如何定义注解,以及注解的属性,那么是不是可以使用注解了呢?莫急~,先了解一波元注解,然后再去使用注解效果更好。

 

四,元注解

那么,什么是元注解呢?元注解也是一个注解,它是用来给普通的注解进行注解,目的是给普通的注解添加限制条件。Java提供了五个元注解,分别是@Retention,@Target,@Inherited,@Documented,@Repeatable。

注意:由于元注解也是一种注解,因此会涉及对注解的使用,注解的使用会在后面详细介绍。

Retention:字面意思是保留期,用于确定普通注解的存活时间。

三种取值如下:

RetentionPolicy.SOURCE:普通注解在源码阶段保留,编译阶段被丢弃;

RetentionPolicy.CLASS :普通注解在编译阶段保留,在运行阶段被丢弃,不会加载进JVM;

RetentionPolicy.RUNTIME:普通注解在运行阶段也保留,它会加载进JVM;

值得一提的是,在使用反射提取注解的时候,由于反射是在运行阶段获取注解,因此该注解必须要添加元注解RetentionPolicy.RUNTIME,否则无法通过反射获取该注解。对注解的提取后面会讲到,这里了解一下即可。

栗子如下:

  1. @Retention(value = RetentionPolicy.RUNTIME)
  2. public @interface Clazz_Annotation {
  3. String name();
  4. int age() default 0;
  5. }

括号里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                             可以给一个类型进行注解,比如类、接口、枚举

栗子如下:

  1. @Retention(value = RetentionPolicy.RUNTIME)
  2. @Target(value = {ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})
  3. public @interface Clazz_Annotation {
  4. String name();
  5. int age() default 0;
  6. }

注解Target的属性value是一个数组类型,给数组设置值,外面用{},多个值之间用逗号隔开。上面我们给它赋值了ElementType.METHOD, ElementType.TYPE, ElementType.FIELD,表示只可以在方法,类/接口/注解,字段上使用该注解。如果在构造方法上使用该注解,则编译错误。

 

Inherited:继承的意思。文字解释太啰嗦,直接看代码。

栗子如下:

  1. @Inherited
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @interface Test {}
  4. @Test
  5. public class A {}
  6. public class B extends A {}

注解Test被@inherited注解,类A被Test注解,且类B继承了类A。如果类B没有被其他注解注解,那么类B也被Test注解。

 

Documented:文档的意思,作用是能够将注解中的元素包含到 Javadoc 中去。

栗子如下:

  1. @Retention(value = RetentionPolicy.RUNTIME)
  2. @Target(value = {ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})
  3. @Documented
  4. public @interface Clazz_Annotation {
  5. String name();
  6. int age() default 0;
  7. }

Documented注解没有属性,查看Documented的源码便知。

 

Repeatable:可重复的意思。被Repeatable注解的普通注解,可以多次使用。

版本:Java1.8引入的元注解。

栗子如下:

  1. public @interface ListAnnotation {
  2. ElementAnnotation[] lists();
  3. }
  4. @Repeatable(ListAnnotation.class)
  5. public @interface ElementAnnotation {
  6. String elment() default "";
  7. }
  8. @ElementAnnotation(elment = "a")
  9. @ElementAnnotation(elment = "b")
  10. @ElementAnnotation(elment = "c")
  11. public class Test {
  12. }

由代码可知:注解ElementAnnotation被@Repeatable注解,且Repeatable注解的属性值为ListAnnotation.class。ListAnnotation是一个注解,它是一个容器注解,用于存放注解ElementAnnotation。需要注意的是,注意注解ListAnnotation的属性为ElementAnnotation类型的数组。

然后使用注解ElementAnnotation给类Test添加注解,而且是3个ElementAnnotation注解,但是注解的属性值不同,分别为"a","b","c"。

 

五,注解的使用

那么如何使用注解呢?我们可以给类/接口/注解,一般方法,构造方法,字段等添加注解。

注意:如需限制某一注解使用的场景,使用元注解@Target即可,前面已经有所分析。

下面以给一个类Test添加注解为例,描述注解的使用。

首先定义一个注解Clazz_Annatation,代码如下:

  1. @Retention(value = RetentionPolicy.RUNTIME)
  2. @Target(value = {ElementType.METHOD, ElementType.TYPE, ElementType.FIELD})
  3. public @interface Clazz_Annotation {
  4. String value() default "";
  5. }

定义一个类Test,使用注解Clazz_Annatation,代码如下:

  1. @Clazz_Annotation(value = "class_Test")
  2. public class Test {
  3. //...
  4. }

使用注解需要给属性赋值,是以括号内(属性名 = 值,属性名 = 值,...)形式呈现,多个属性的赋值以逗号隔开。本例中,由于value属性在注解中设置了默认值"",使用注解时可以不给value赋值。

如果注解只有一个value属性,可以简写为:@Clazz_Annotation("class_Test");

如果注解只有一个非value的属性,不可以简写,仍需要以(属性名 = 值)的格式;

如果注解没有属性,可以简写为:@Clazz_Annotation;

 

六,注解的提取

前面介绍了注解的使用,有木有发现注解并没有起到具体的作用,我们并不知道这样注解后有啥效果,于是涉及到注解的提取。注解的提取需要用Java的反射技术,在运行阶段获取注解。获取了注解,就获取了注解的属性相关的信息。

下面粘贴一个栗子,分别提取了类,字段,方法的注解,代码如下:

  1. @Clazz_Annotation("class_Test")
  2. public class Test {
  3. @Clazz_Annotation("field_obj")
  4. public Object obj = null;
  5. @Clazz_Annotation("method_main")
  6. public static void main(String[] args) {
  7. print();
  8. }
  9. private static void print() {
  10. //----class-----------
  11. boolean isExist = Test.class.isAnnotationPresent(Clazz_Annotation.class);
  12. if (isExist) {
  13. Clazz_Annotation clazz_Annotation = Test.class.getAnnotation(Clazz_Annotation.class);
  14. String value = clazz_Annotation.value();
  15. System.out.println("value = " + value);
  16. } else {
  17. System.out.println("Clazz_Annotation is not exist on class");
  18. }
  19. //------field-------
  20. try {
  21. Field field = Test.class.getField("obj");
  22. boolean isExist1 = field.isAnnotationPresent(Clazz_Annotation.class);
  23. if (isExist1) {
  24. Clazz_Annotation clazz_Annotation1 = field.getAnnotation(Clazz_Annotation.class);
  25. String value1 = clazz_Annotation1.value();
  26. System.out.println("value = " + value1);
  27. } else {
  28. System.out.println("Clazz_Annotation is not exist on field");
  29. }
  30. } catch (Exception e) {
  31. e.printStackTrace();
  32. }
  33. //-----method----------
  34. try {
  35. Method method = Test.class.getMethod("main", String[].class);
  36. boolean isExist2 = method.isAnnotationPresent(Clazz_Annotation.class);
  37. if (isExist2) {
  38. Clazz_Annotation clazz_Annotation2 = method.getAnnotation(Clazz_Annotation.class);
  39. String value2 = clazz_Annotation2.value();
  40. System.out.println("value = " + value2);
  41. } else {
  42. System.out.println("Clazz_Annotation is not exist on method");
  43. }
  44. } catch (Exception e) {
  45. e.printStackTrace();
  46. }
  47. }
  48. }

上述代码分别给类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的反射机制,在运行阶段先获取注解,然后获取注解的属性。

在运行阶段使用反射技术,对性能会有一定的损耗,毕竟反射没有直接调用节约时间,这也是反射提取注解的缺点。

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/804976
推荐阅读
相关标签
  

闽ICP备14008679号