java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java 注解原理

Java 注解的底层原理解析(java基础面试必问)

作者:Lyyaoo.

本文介绍了Java注解的基本概念及其生命周期、使用位置等元注解的定义和使用方法,详细解释了注解的在编译、类加载和运行时的处理过程,并通过实例说明了注解在反射API中的实现原理,感兴趣的朋友跟随小编一起看看吧

前言

注解是 Java 5 引入的一种元数据机制,可以在代码中添加信息,这些信息可以被编译器、开发工具或运行时环境使用。

注解的本质是一个继承了Annotation的接口

 Java 注解(Annotation)是一种元数据,它本身不直接影响代码逻辑,但可以被编译器、工具或框架在编译时或运行时读取并处理。

 注解实际上是一个接口,它隐式地继承自 java.lang.annotation.Annotation 接口。注解中定义的方法对应注解的“属性”。这些方法没有参数,返回值类型受限(基本类型、String、Class、枚举、注解以及它们的数组)。

 对于一个简单的自定义注解,使用 @interface 关键字进行实现

public @interface MyAnnotation {
    String value() default "";
}

 用 javap -c MyAnnotation 反编译后,会看到,

public interface MyAnnotation extends java.lang.annotation.Annotation {
  public abstract java.lang.String value();
}

元注解

 Java 提供了几个元注解(即注解的注解),用来定义注解的生命周期、使用位置等。

元注解作用
@Target指定注解可以用在哪些地方(类、方法、字段、参数等)。
@Retention指定注解保留到哪个阶段(源码、字节码、运行时)。
@Documented是否包含在 Javadoc 中。
@Inherited是否允许子类继承父类的注解。
@Repeatable(Java 8)允许同一位置重复使用同一个注解。

@Target注解

 @Target 接收一个 ElementType 数组,表示该注解可以出现在哪些地方。常用的 ElementType 枚举值包括:

ElementType说明
TYPE类、接口、枚举、注解类型
FIELD成员变量(包括枚举常量)
METHOD方法
PARAMETER方法参数
CONSTRUCTOR构造方法
LOCAL_VARIABLE局部变量
ANNOTATION_TYPE注解类型(用于元注解)
PACKAGE
TYPE_PARAMETER(Java 8)类型参数(如 class MyClass 中的 T)
TYPE_USE(Java 8)类型使用处(如 new @NonNull String())

@Retention注解

注解在字节码中的存储

编译阶段

 当编译器处理带有注解的代码时,会根据 @Retention 决定是否将注解信息写入 .class 文件。对于 RUNTIME 或 CLASS 级别的注解,编译器会在字节码中添加专门的属性表(Attribute)。

 以 @MyAnnotation 标注一个类为例:

@MyAnnotation("hello")
public class Test {}

 用 javap -v Test 查看字节码,会看到类似:

RuntimeVisibleAnnotations:
  0: #10(#11=s#12)
    #10 = Utf8 "LMyAnnotation;"
    #11 = Utf8 "value"
    #12 = Utf8 "hello"

 RuntimeVisibleAnnotations 是字节码中的一种属性,表示在运行时可见的注解列表。每个注解被编码为:注解类型 + 属性名 + 属性值。例子中的类型为 LMyAnnotation;,属性名是value,属性值是hello。

类加载阶段

 JVM 在加载类时,会读取 .class 文件中的这些属性,将注解信息解析并存储到类的元数据中(方法区的 Annotation 数据结构)。但对于 @Retention(CLASS) 的注解,在类加载后这些信息会被丢弃;而对于 RUNTIME 的注解,会保留在运行时。

运行时注解(反射API)

JVM自动生成动态代理对象来实现注解接口,可通过代理对象的 invoke 方法实现对注解中属性对应值的返回(自定义注解中的 value 的对应属性值)

 当注解的 @Retention 为 RUNTIME 时,才可以通过反射 API 获取注解信息

Annotation[] annotations = Test.class.getAnnotations();
MyAnnotation myAnno = Test.class.getAnnotation(MyAnnotation.class);
String value = myAnno.value();

@MyAnnotation(“hello”) 本质上是 @MyAnnotation(value = “hello”),相当于给value赋值为 “hello”,调用myAnno.value()本质上调用代理对象的 invoke 方法,最终返回属性值。

到此这篇关于【Java基础面经】Java 注解的底层原理的文章就介绍到这了,更多相关Java 注解原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文