Java注解基础介绍及使用

Java注解基础介绍及使用

一、什么是注解

1、官方定义

Java注解又称Java标注,是Java语言5.0版本开始支持加入源代码的特殊语法元数据。Java语言中的类、方法、变量、参数和包等都可以被标注。和Javadoc不同,Java标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java虚拟机可以保留标注内容,在运行时可以获取到标注内容。

维基百科对java注解说的还是比较到位了,不理解可以先看看后面,再回来看上面的定义就很清楚了。

2、通俗说法


在Android中,注解的使用无处不在,比如上图中的 @Override@Nullable,注解实际就是添加在类、变量、方法、参数等前面的一个修饰符而已,可以理解成一个标签。

注解和注释不一样,注释只能保留在源码阶段,用来对代码的解释或者说明,可以看做就是文本,但是注解不一样,它可以保留到运行阶段,我们可以把注解也理解成代码,注释性代码,它除了提供了对代码的解释说明,它还能跟代码一样提供功能操作,但它是被动性的,就是它能提供信息,但是需要我们通过其他方式来获取,比如可以通过其他方式如反射在动态运行阶段获取注解的信息并使用。

3、注解怎么写?

我们直接点 @Override 注解进去看看就知道,一个注解是什么定义的。

这是@Override 注解的写法,先不管A和B,这个我们后面会说,直接先看D,一个注解,需要使用 @interface,就跟我们的类需要用class修饰,接口用interface修饰一样,只要用@interface修饰的就是注解。

所以,一个简单的注解可以如下定义。

public @interface TestAnnotation {}

使用:

4、元注解

再看上图,还有A@Target 和B@Retention两个“注解”,这种用来注解注解的注解我们叫做元注解,如果注解理解成是一个标签的话,那么元注解就是比较特殊的标签,它只能用来对普通标签进行解释说明。

元注解有 @Retention、@Documented、@Target、@Inherited、@Repeatable 5 种。

4.1、@Retention

Retention 的英文意为保留期的意思。当 @Retention 应用到一个注解上的时候,它解释说明了这个注解的的存活时间。

它有一下三种取值:

  • RetentionPolicy.SOURCE :
    注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。

使用场景:这类注解提供信息给编译器,编译器可以利用注解来探测错误和警告信息,比如我们的 @Override

  • RetentionPolicy.CLASS :
    注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。

使用场景:软件工具可以利用注解信息来生成代码,Html文档和做其他相应处理。

  • RetentionPolicy.RUNTIME :
    注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时

使用场景:可以获取到它们的信息并进行逻辑处理(这也是我们经常用的,会配合反射使用)。

大概示例图如下:

4.2、@Target


Target 英语就是目标的意思,@Target 指定了注解可以运用的地方,我们前面说了注解可以用在类、方法、变量、参数和包等上面,如果我们没有使用@Target 元注解对注解进行解释说明,那么这个注解可以随意放在类、方法变量这些地方,我们使用了@Target后 ,通过指定其可以运用的地方,规定其只能用在我们指定的上。

@Target 参数是一个数组枚举 ElementType[]

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,  //  接口、类、枚举、注解

    /** Field declaration (includes enum constants) */
    FIELD, // 字段、属性、枚举的常量

    /** Method declaration */
    METHOD, // 方法

    /** Formal parameter declaration */
    PARAMETER,// 方法参数

    /** Constructor declaration */
    CONSTRUCTOR, //构造函数

    /** Local variable declaration */
    LOCAL_VARIABLE, //局部变量

    /** Annotation type declaration */
    ANNOTATION_TYPE, //注解
    
    /** Package declaration */
    PACKAGE, //包   

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

使用:

@Target(ElementType.FIELD) // 只能注解到一个属性上。

@Target({ElementType.FIELD,ElementType.METHOD,ElementType.PACKAGE}) // 只能注解到属性、方法,包上。
4.3、@Documented

标记注解,没有成员,顾名思义,这个元注解肯定是和文档有关。它的作用是能够将注解中的元素包含到 Javadoc 中去。

参考 Annotation深入研究——@Documented注释使用

4.4、@Inherited

Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注解。

下面简单通过一个Demo来演示一下:

  • 首先定义一个注解,并使用@Inherited元注解

  • 定义一个Human类,使用我们自定义注解@Test
  • 新建一个Man类继承至Human类。
  • 我们在Man类中通过反射获取其是否也拥有@Test注解
  • 从结果可以看出来,在Man类也能获取到@Test注解和参数,这就是因为其父类上的注解使用了@Inherited元注解
4.5、@Repeatable

字面意思是可重复的,说明这个元注解可重复使用,我们看到@Repeatable注解的变量类型则是对应Annotation(接口)的泛型Class。

  • 我们先定义一个重复注解类:

  • 再声明一个容器注解类

  • 我们就可以对@Tag注解重复使用,可以看到没有使用@Repeatable的注解重复使用就会报错

注解的属性

通过前面的示例我们可以看到,我们可以为我们的注解添加属性,注解的属性也叫做成员变量。注解只有成员变量,没有方法。

注解的成员变量在注解的定义中以“无形参的方法”形式来声明,

其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型

反射中,通过getDeclaredMethod来获取。

在注解中定义属性时它的类型必须是 8 种基本数据类型外加 类、接口、注解及它们的数组
注解中属性可以有默认值,默认值需要用 default 关键值指定。

前面基本已经演示过了,我们还是简单演示一下。

定义一个带有多个成员变量的注解:


/**
 * @author : EvanZch
 *         description:定义多个成员变量注解
 **/

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestValueAnnotation {
    String name() default "EvanZch";

    int age();

    int[] info();
}

使用:

@TestValueAnnotation(name = "Evan", age = 18, info = {175, 65})
public class AnnotationValueTest {

    public static void main(String[] args) {
    }

}

注解提取

注解通常与反射一起使用,而反射就是在运行状态中,对于任意一个类,都能知道这个类的所有属性及方法,对于任何一个对象,都能调用他的任何一个方法和属性,这种动态获取新的及动态调用对象的方法的功能叫做反射.

对反射不熟悉的可以先看看Java反射以及在Android中的使用

注解通过反射获取。首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解

public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}

然后通过 getAnnotation() 方法来获取 Annotation 对象。

 public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}

或者是 getAnnotations() 方法。

public Annotation[] getAnnotations() {}

前一种方法返回指定类型的注解,后一种方法返回注解到这个元素上的所有注解。

我们通过前面的AnnotationValueTest类演示,获取AnnotationValueTest类上@TestValueAnnotation注解的信息。

  • 注释尽可能的详细了,如果还不清楚可以先去看看反射。
public static void main(String[] args) {
// 一、判断AnnotationValueTest类上是否有注解
// 1、先拿到AnnotationValueTest的class对象
        Class<?> annotationValueTestzz = AnnotationValueTest.class;
 // 2、通过其class对象调用isAnnotationPresent进行判断
        boolean isAnnotation = annotationValueTestzz.isAnnotationPresent(TestValueAnnotation.class);
        System.out.println("AnnotationValueTest--isAnnotation=" + isAnnotation);
// 如果有注解
if (isAnnotation) {
// 3、有注解拿到这个注解的Annotation对象
       TestValueAnnotation annotation = annotationValueTestzz.getAnnotation(TestValueAnnotation.class);
 // 4、通过annotation对象获取其成员变量
            int age = annotation.age();
            int[] infos = annotation.info();
            String name = annotation.name();
            System.out.println("AnnotationValueTest--age=" + age + ",info.length=" + infos.length + ",name=" + name);
  }

结果:


可以看到,我们成功判断到AnnotationValueTest类上的@TestValueAnnotation注解中的所有属性信息。

总结

关于注解的使用,在Android中很常见,比如我们butterknife和retrofit库里面就大量使用注解来进行操作,我们下一篇,通过一篇注解实战来写一个简易版本的butterknife,下一篇见!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章