Java 注解与反射

Always learn from the beast!
学习是一个螺旋前进的过程,先总览全部的知识,留个印象,在此基础上绘制学科整体框架图。
按照框架图的内容去学习每个细分支类,在每个支类同样绘制支类总体框架图,总览知识点分布。
框架图层级的明确可以让自己时刻知道在学什么,这个知识有什么意义。
学习了一定的细分项目之后又回头总揽全局,防止钻牛角尖,就这样螺旋前进。

1. 注解

java注解的官方文档:
Java 注解用于为 Java 代码提供元数据。
作为元数据,注解不直接影响你的代码执行,但也有一些类型的注解实际上可以用于这一目的。

老实说,这是一份对于新手来说糟糕的说明,因为它用 术语解释术语 这导致了难以理解。

1.1 注解的基本信息

(一)到底什么是注解?
注解是一张标签,用于表示被注解代码块的各种性质。

(二)元注解:

  • 元注解是注解的注解,是一张特殊的标签,即标签的标签。
  • 元注解是用于给其他普通标签进行解释说明的。
  • 元标签有:@Retention, @Documented, @Targrt, @Inherited, @Repeatable 共5种。

1.2 元标签详解

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

它的取值如下:

  • RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
  • RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到 JVM 中。
  • RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。

我们可以这样的方式来加深理解,@Retention 去给一张标签解释的时候,它指定了这张标签张贴的时间。
@Retention 相当于给一张标签上面盖了一张 时间戳,时间戳指明了标签张贴的时间周期。

(2)@Documented
顾名思义,这个元注解肯定是和文档有关。它的作用是能够将注解中的元素包含到 Javadoc 中去。

(3)@Target
Target 是目标的意思,@Target 指定了注解运用的地方。
当一个注解被 @Target 注解时,这个注解就被限定了运用的场景。
类比到标签,原本标签是你想张贴到哪个地方就到哪个地方,但是因为 @Target 的存在,它张贴的地方就非常具体了,比如只能张贴到方法上、类上、方法参数上等等。

@Target 有下面的取值:

  • ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
  • ElementType.CONSTRUCTOR 可以给构造方法进行注解
  • ElementType.FIELD 可以给属性进行注解
  • ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
  • ElementType.METHOD 可以给方法进行注解
  • ElementType.PACKAGE 可以给一个包进行注解
  • ElementType.PARAMETER 可以给一个方法内的参数进行注解
  • ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举

(4)@Inherited
Inherited 意为继承。
具体来说,当父类注解具有此元注解时,子类无任意注解,那么此时子类继承父类该注解。
比如:父类存在注解A(注解A被元注解@Inherited注解),子类无任何注解,那么子类继承注解A。
再比如:王健林有富豪标签,那么它儿子也有富豪标签,孙子也是。

(5)@Repeatable
用于标注可重复的注解。拥有该元注解的可能是一张总标签(包含众多标签属性的集合标签)
什么样的注解会被多次应用呢?通常是注解的值可以同时取多个。
例如,一个人既是程序员又是产品经理还是画家。

@interface Persons {
    Person[]  value();
}
@Repeatable(Persons.class)

@interface Person{
    String role default "";
}

@Person(role="artist")
@Person(role="coder")
@Person(role="PM")

public class SuperMan{
}

注意上面的代码,@Repeatable 注解了 Person。而 @Repeatable 后面括号中的类相当于一个容器注解。
什么是容器注解呢?就是用来存放其它注解的地方。它本身也是一个注解。
我们再看看代码中的相关 容器注解。

@interface Persons {
    Person[]  value();
}

如果不好理解的话,可以这样理解。Persons 是一张总的标签,上面贴满了 Person 这种同类型但内容不一样的标签。
把 Persons 给一个 SuperMan 贴上,相当于同时给他贴了程序员、产品经理、画家的标签。
我们可能对于 @Person(role=”PM”) 括号里面的内容感兴趣,它其实就是给 Person 这个注解的 role 属性赋值为 PM .

1.3 注解的属性(重要)

注解的 属性 也叫做 成员变量 。注解只有成员变量,没有方法。
注解的 成员变量 在注解的定义中 以“无形参的方法”形式来声明,其 方法名 定义了该成员变量的名字,其 返回值 定义了 该成员变量的类型 。
注解中 定义属性 时它的类型必须是** 8 种基本数据类型外加 类、接口、注解及它们的数组。**
注解中属性可以有默认值,默认值需要用 default 关键值指定。

举例说明注解定义属性:

@Target(ElementType.TYPE)  // 表示可以给一个类型进行注解,如接口/类/枚举等
@Retention(RetentionPolicy.RUNTIME)  // 注解可以保留到程序运行的时候,它会被加载进入到 JVM 中,所以在程序运行时可以获取到它们。
public @interface TestAnnotation {
    int id() default -1;  // 定义默认值
    String msg() default "Hi";  // 定义默认值
}

上面代码定义了注解 TestAnnotation 。这个注解中拥有 id 和 msg 两个属性。
在调用该注解时,我们应该给属性进行赋值(即赋予实参)。
赋值的方式是在 注解的括号 内以 键值对”key1=value1, key2=value2, key3=value3, ...” 形式赋值;多个属性之间用 ","隔开。
若只有一个属性,则无项键值对,直接括号内赋值即可。
若注解无任何属性,则调用该注解时可以省略括号。
属性调用举例:

@TestAnnotation(id=3,msg="hello annotation")  // 括号内为调用注解的 属性赋值
public class Test {
}

1.4 注解与反射

Java程序种,注解通过反射获取。

  1. 首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解。
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}
  1. 然后通过 getAnnotation() 方法来获取 Annotation 对象。
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}  // 该方法返回指定类型的注解
  1. 也可以是 getAnnotations() 方法。
public Annotation[] getAnnotations() {}  // 该方法返回注解到该元素上的所有注解
  1. 如果获取到的 Annotation 如果不为 null,则就可以调用它们的属性方法了。
@TestAnnotation()  // 我们上一小节写的一个小注解,含有两个属性。
public class Test {
    public static void main(String[] args) {
        boolean hasAnnotation = Test.class.isAnnotationPresent(TestAnnotation.class);
        if ( hasAnnotation ) {
            TestAnnotation testAnnotation = Test.class.getAnnotation(TestAnnotation.class);
            System.out.println("id:"+testAnnotation.id());
            System.out.println("msg:"+testAnnotation.msg());
        }
    }
}

// 运行返回结果:
// id: -1, msg: 
// 这就是默认值

1.5 注解的作用与意义

注解的作用:

  • 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息。
  • 编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
  • 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取。
  • 值得注意的是,注解不是代码本身的一部分。

2. 反射

  • 反射(Reflection):Java的反射是指程序在运行期可以拿到(任何)一个(类)对象的所有信息。
  • Java的反射机制是指在程序的运行状态中:
    • 可以 构造 任意一个类的 对象。
    • 可以 了解 任意一个对象 所属的类。
    • 可以 了解 任意一个类 的 成员变量和方法。
    • 可以 调用、操作 任意一个对象的 属性和方法。
  • 上述这种 动态获取程序信息以及动态调用对象 的功能称为Java语言的反射机制。
  • 反射被视为动态语言(在程序运行的时候可以改变其结构)的关键。
posted @ 2022-08-10 14:23  青梧成林2022  阅读(85)  评论(0)    收藏  举报