java的Class文件分析

文章目录

    • 1. 简介
    • 2. Class文件分析

1. 简介

Java有一个著名的口号一次编译,处处运行,这就凸显出来Java程序的一个特点平台无关性。Java的平台无关性是基于各种不同平台的Java虚拟机,以及所有平台都统一支持的程序存储格式—字节码实现的。在Java中任何一个Class文件都对应一个类或接口的定义信息。Class文件是以一组8个字节为单位的二进制流,各个数据项目严格按照顺序紧凑地排列在文件中,中间没有添加任何分隔符,当遇到需要占用8个字节以上空间的数据项时,会按照高位在前的方式分割成若干个8个字节进行存储。

2. Class文件分析

首先我们将下面代码编译成字节码:

public class testJVM {
    private int m;
    public int inc(){
        return m+1;
    }
}

生成的字节码文件如下:
在这里插入图片描述

  • 魔数(Magic)

每个Class文件的头4个字节被称为魔数,它唯一的作用是确定这个文件是否为一个能被虚拟机接受Class文件(即判断Class文件是否符合JVM标准),Java的Class文件的模式数的默认值是0xCAFEBABE

在这里插入图片描述

  • 版本号(Minor_version)

魔术的后面四个字节是Class文件的版本号,第5个和第6个字节是次版本号(Minor Version),第7第八个字节是主版本号(Major Version)。Java的版本号是从45开始的,JDK1.1之后的每个JDK大版本发布主版本号加1。
在这里插入图片描述
可以发现次版本号为0000,主版本号为0034,主版本号转化为10进制为52,也就是说明这个版本是可以被JDK 8或以上版本的虚拟机执行Class文件的(jdk(1+52-45))。

  • 常量池(constant_pool_count)

在版本号后面就是常量池入口,常量池是Class文件的资源仓库,它是Class文件结构中与其它项目关联最多的数据,通常也是Class文件空间中最大的数据项之一。由于常量池中常量的数量是不固定的,所以在常量池的入口需要放置一个u2(2个字节的无符号整数)来代表常量池的容量。

在这里插入图片描述
可以看出常量池的大小为0x0016,即十进制22,代表常量池中有21项常量,索引范围是1~21。思考:为什么不从1开始计数,其实Class文件中的其他集合类型,包括接口索引集合、字段表集合、方法表集合等都是从0开始的?,Class文件这么设计是有目的,设计者将第0项常量空出来是为了如果后面某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池的项目”的含义,可以把索引值设置为0来表示。

常量池中主要存放两大类常量:字面量和符号引用,字面量比较接近Java语言层面的常量概念,如文本字符串、被声明的final的常量值等。而符号引用属于编译原理的概念,主要有以下常量:

  1. 被模块导出或者开放的包
  2. 类和接口的全限定名
  3. 字段的名称和描述符
  4. 方法的名称和描述符
  5. 方法句柄和方法类型
  6. 动态调用点和动态常量

我们直接分析class文件好像不太好理解,我们可以使用JDK中的javap工具来数据class文件的字节码内容。

 javap -verbose /Users/jackchai/Desktop/Self-study-notes/算法与数据结构/手写算法/jackchai/target/classes/com/jack/subject/util/testJVM.class

在这里插入图片描述
可以发现常量池有21个常量,和我们前面分析的一样,上面的内容在分析字节码的时候是很重要的,Java代码在进行Javac编译的时候,并不像C和C++那样有连接这一步骤,而是在虚拟机加载Class文件的时候进行动态连接,也就是说,在Class文件中不会保持各个方法、字段最终在内存中的布局信息,这些字段、方法的符号引用不经过虚拟机在运行期进行转换的话是无法得到真正的内存入口地址的,也就无法直接被虚拟机使用的。当虚拟机进行类加载的时候,将会从常量池中获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址之中。

常量池中的每一项常量都是一个表,最初常量表中共有11种结构各不相同的表结构数据,后来为例更好的支持动态语言调用,额外增加了4种动态语言相关的常量。为了更好的支持模块化系统,又加入了CONSTANT_Module_infoCONSTANT_Package_info两个常量,所以截止到JDK 13,常量表中分别有17中不同类型的常量。

在这里插入图片描述
回到我们的案例中,第一个常量为0x0a,查表的10行,发现这是个类中方法的符号引用(CONSTANT_Methodref_info)。我们查看Javap的结构,发现第一个常量的引用为java/lang/Object."<init>":()V,这个是编译器自动生成的方法。从后面的表我们可以看出这种类型的常量需要占用5个字节。所以第一个常量在class文件中的位置如下:

在这里插入图片描述
然后第二个常量的位置为0x09,为Const_Fieldref_info,然后才Class文件中的位置如下:
在这里插入图片描述

后面的以此类推,就可以得出整个常量池的情况,如下图所示:
在这里插入图片描述
可以发现常量池在Class文件中占的比重还是比较大的。

常量池中的17种数据类型的结构总表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

  • 访问标志

在常量池结束之后,紧接着是2个字节代表访问标志(access_flag),这个标志用来标志由于你识别一些类或者接口的访问信息,包括这个Class是类还是接口,是否定义为public类型;是否被定义为abstract类型;如果是类的话,是否被声明为final等等。

扫描全能王 2024-04-01 08.27_1

access_flag一共有16个标志位可以使用,目前只使用了9种,没有使用到的一律为0。我们看一下我们案例的访问标志位:
在这里插入图片描述
0x21,即0x001|0x20,所以查表得我们的类的ACC_PUBLICACC_SUPER标志位为真。

  • 类索引、父类索引与接口索引集合

类索引和父类索引都是一个u2类型的数据,而接口索引集合是一组u2类型的数据的集合,Class文件中由这三项数据来确定该类型的继承关系。类索引确定这个类的全限定名,父类索引确定这个类的父类的全限定名,由于Java语言不允许多继承所以父类索引只有一个,除了Java.lang.Object之外,所以的类都父类,因此出类Object外所有Java类的父类索引都不为0。接口索引集合描述这个类实现的接口,这些被实现的接口按照implents顺序排列在接口索引集合中。

我们回到我们的案例,这部分的字节码为:
在这里插入图片描述
首先是类索引为0x0003,到我们常量池中找,可以找到这个索引指向的常量为com/jack/subject/util/testJVM。然后父类索引为0x0004查找常量池为java/lang/Object,所以父类为Object类为。最后由于我们的测试类没有实现任何接口,所以接口索引集合为0x00(前面说过设计者将第0项的常量空出来是为了表示不引向任何一个常量池项目的含义)。

  • 字段表集合

字段表集合用于描述接口或者类中声明的变量。Java语言中的字段包括类级的变量和实例级变量,但不包括方法内部声明的局部变量。字段表的结构大体如下所示:

在这里插入图片描述
首先是access_flags,这个和前面类的访问标志的结构是很类似的,都是一个u2的数据类型。

在这里插入图片描述

访问标志后面就是两项索引值,name_index和descriptor_index。它们都是对常量池项的引用,分别代表着字段的简单名称以及字段和方法的描述符。这里我们比较一下“简单名称”“描述符”和“全限定名”三个字符串的概念。全限定名和简单名称很好理解,如前面的 com/jack/subject/util/testJVM,就是把类全名的.替换成了/而已,为了使连续多个全限定名之间不产生混淆,在最后一般会加入;符号表示一个全限定名的结束。简单名称就是指没有类型和参数修饰的方法和字段的名称,如这个类中的inc方法和m字段的简单名称就是incm。相比于全限定名和简单名称,方法和字段的描述符要多一些,描述符的作用是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。根据描述符的规则,基本数据类型以及代表有无返回值的void类型第一个字母都要大些,而对象类型要用字符L加对象的全限定名来表示。

在这里插入图片描述
对于数组类型,每一个维度将使用一个前置的"["字符来表示,如java.lang.String[][]类型会被记录为[[Ljava/lang/String。用描述符来描述方法时,按照先参数列表、后返回值的顺序,如()V。回到我们的案例:

在这里插入图片描述

首先是0x0001说明这个类只有一个字段表数据。接下来是0x0002代表为ACC_PRIVATE,然后name_index就是0x0005,我到常量池中可以看到为m,然后descriptor_index的值为0x0006,回到常量池中可以看到为I即为int类型。到此字段表包含的固定数据项目到此结束,不过descriptor_index后面之后会跟随一个属性表集合,用来存储一些额外的信息。对于本例中它的属性计数器为0,也就是没有额外信息,如果m的声明改为final static int m=123,那就可能存在一项名称为Constant_value的属性,其值指向123。这里就不详细分析这一部分了。

字段表集合汇总不会列出从父类或者父类接口中基础而来的字段,但是有可能出现原本Java代码之外不存在的字段,譬如在内部类为了保持对外部类的访问性,编译器就会自动添加指向外部类实例的字段,另外在Java中字段是无法重载的,两个字段的数据类型不管是否相同,都必须使用不一样的名称,但是对于Class文件来讲,只要两个字段的描述符不是完全相同,那字段重名是合法的。

  • 方法表集合

Class文件对于方法的描述与对字段的描述集合采用了完全一致的方法。

在这里插入图片描述
它的访问标志有下面几种:

在这里插入图片描述
回到我们的案例:

在这里插入图片描述

第一个u2类型的数据为0x00002,代表集合中有两个方法,首先第一个方法的访问标志值为0x0001,也就是ACC_PUBLICname_index0x0007,我们到常量池中一看是<init>descriptor_index0x0008表示()V。然后是属性计数器为0x0001表示这个方法属性集合中有一项属性,属性名称为0x0009,为Code,说明此属性是方法的字节码描述。

与字段表集合对应地,如果父类方法在子类方法中没有重写,方法表集合中就不会出现来自父类的方法信息,但同样的会出现由编译器自动添加的方法,最常见的便是类构造器<clinit>()方法和实例构造器<init>()方法。在Java语言中要重载一个方法,除了要与原方法具有相同的简单名称之外,还必须要有一个与原方法不同的特征签名。特征签名是指一个方法中各个参数在常量池中的字段符号引用的集合,也正是因为返回值不会包含在特征签名中,所以Java语言是无法仅仅依靠返回值来对一个已有的方法进行重载的。但是Class文件中可以使用返回值区分。

  • 属性表集合

属性表大家也可以看到前面已经出现过很多次了,Class文件、字段表和方法表都有自己的属性表集合,以描述某些特有场景。

在这里插入图片描述
在这里插入图片描述
属性表的结构如下:
在这里插入图片描述
我们挑一些重点属性来分析:

1. Code属性

Java程序的方法体里面的代码经过Javac编译器处理后,最终变为字节码指令存储载Code属性中。Code属性出现在方法表的属性集合中,但并非所有的方法表都必须存砸这个属性,例如接口就没有Code属性。

在这里插入图片描述

  • attribute_name_index:指向Constant_Utf8_info型常量的索引,代表属性的属性名称,固定为Code(2个字节)
  • attribute_length:表示属性值的长度(4个字节)
  • max_stack:代表操作数栈的深度最大值,JVM根据这个值来分配栈帧
  • max_locals:代表局部变量表所需的存储空间,单位是变量槽(变量槽是JVM为局部变量分配内存最小单位),32位的数据类型如byte占用一个变量槽,64位如double和long占用两个变量槽。显示异常处理程序的参数(try-catch),方法参数(包括实例方法中的隐藏参数this),方法体定义的局部变量都是用局部变量表存储的,局部变量表占用的变量槽的数量就是max_locals的值。操作数栈和局部变量表直接决定了一个栈帧占用的内存。注意Java虚拟机会对变量槽进行重用,例如代码执行超过了一个局部变量表的作用域时,这个剧本变量的变量槽就可以被重用,JVM会根据同时生存的最大局部变量数量和类型计算max_locals大小
  • code_length和code:用来存储Java源程序编译后生成的字节码指令。code_length代表字节码长度,code就是真正的字节码类型。COde属性是Class文件中最重要的一个属性,如果把一个Java代码中的信息分为代码和元数据类型,那么Code就是描述代码,其他数据项目用于描述元数据。

回到案例,我们继续向后分析Class文件:

在这里插入图片描述
首先操作数栈的最大深度和本地变量表的容量都是0x0001,字节码长度为0x00000005,虚拟机读区到的字节码就是后面的5个字节0x2ab70001b1。加入JVM需要执行这个方法,那么过程描述如下:

  1. 读入2a:0x2a对应的指令为aload_0,这个指令的含义是将第0个变量槽中为reference类型的本地变量推松到操作数栈的栈顶
  2. 读入b7:对应指令为invokespecial,这条指令的作用是以栈顶的refrence类型的数据所指向的对象为方法接收者,调用此对象的实例构造方法,private方法或者它父类的方法。这个方法有一个u2类型的参数说明具体调用哪个方法,它指向常量池的一个CONSTANT_Methodref_info类型常量,即此方法的符号引用
  3. 读入0001:这个invokespecial指令的参数,代表一个符号引用,可以从常量池中可以看出是java/lang/Object."<init>":()V即构造方法的符号引用。
  4. 读入B1:代表return指令,表示方法的返回

我们看Javap指令对字节码的计算结果,可以看出这个方法的大体结构:
在这里插入图片描述
注意不管实例方法有没有参数args_size都是从1开始算的,这是因为在任何实例方法中,都可以通过this访问到此方法所属的对象。因此在实例方法中的局部变量表中至少会存在一个指向当前对象实例的局部变量表。现在我们分析一下如果方法存在异常,那么方法的字节码结构又会变成什么样子,我们修改一下inc方法代码;

public class testJVM {
    private int m;
    public int inc(){
        int x;
        try{
            x=1;
            return x;
        }catch (Exception a){
            x=2;
            return x;
        }finally {
            x=3;
        }
    }
}

现在inc方法的字节码计算出来就是下面这种情况:

 public int inc();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=5, args_size=1
         0: iconst_1
         1: istore_1
         2: iload_1
         3: istore_2
         4: iconst_3
         5: istore_1
         6: iload_2
         7: ireturn
         8: astore_2
         9: iconst_2
        10: istore_1
        11: iload_1
        12: istore_3
        13: iconst_3
        14: istore_1
        15: iload_3
        16: ireturn
        17: astore        4
        19: iconst_3
        20: istore_1
        21: aload         4
        23: athrow
      Exception table:
         from    to  target type
             0     4     8   Class java/lang/Exception
             0     4    17   any
             8    13    17   any
            17    19    17   any
      LineNumberTable:
        line 10: 0
        line 11: 2
        line 16: 4
        line 11: 6
        line 12: 8
        line 13: 9
        line 14: 11
        line 16: 13
        line 14: 15
        line 16: 17
        line 17: 21
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            2       6     1     x   I
            9       8     2     a   Ljava/lang/Exception;
           11       6     1     x   I
            0      24     0  this   Lcom/jack/subject/util/testJVM;
           21       3     1     x   I
      StackMapTable: number_of_entries = 2
        frame_type = 72 /* same_locals_1_stack_item */
          stack = [ class java/lang/Exception ]
        frame_type = 72 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]
}

可以发现多了一个Exception table即异常表,异常表的结构如下所示:
在这里插入图片描述
如果当字节码从start_pc行到end_pc行之间(不包含end_pc行)出现了来信为catch_type或者其子类的异常(catch_type为指向一个CONSTANT_Class_info型常量的索引),则转到handler_pc行继续处理,当catch_type为0时,代表任意异常都需要转到handler_pc进行处理。

2. Exceptions属性

这里的Exceptions属性是在方法中与Code平级的属性,不要和前面的异常产生混淆,Exceptions属性的作用时列举出方法中可能抛出的受检查异常(Checked Exceptions),也就是方法描述时在throws关键字后面的异常。它的结构如下:
在这里插入图片描述

3. LineNumberTable属性

用于描述Java源码行与字节码行号(字节码偏移量)之间的对应关系(断点机制和这个有关,有利于程序调试)。它的结构如下:
在这里插入图片描述
4. LocalVariableTable和LocalVariableTypeTable属性

前者用于描述栈帧中局部变量表与Java源码中定义的变量的关系,如果没有设置这个属性,别人引用这个方法时,所有参数名会丢失。其结构如下:
在这里插入图片描述

其中local_variable_info就是代表了一个栈帧与源码中局部变量之间的关联关系,它也有自己的结构:

在这里插入图片描述

  • start_pc和length属性:代表这个局部变量的生命周期的字节码偏移量和作用的范围,通过这两个值JVM可以知道局部变量在字节码中的作用域
  • name_index和descriptor:指向常量池,代表该局部变量的名称以及这个局部变量的描述符
  • index:就是这个局部变量在栈帧中的变量槽的位置

在JDK 5引入泛型后,LocalVariableTable属性新增量一个姐妹属性“LocalVariableTypeTable”这个新增的属性结于LocalVariableTable非常类似,仅仅是把记录字段描述符的descriptor_index替换成了字段的特征签名,对于非泛型来说,描述符和特征签名能描述的信息是能吻合的,但是泛型引入后,由于描述符的参数化类型被擦除,描述符就不能准确描述访型信息了,所以出现了这个属性,使用字段的特征签名来完成泛型的描述。

5. SourceFile属性

SourceFile用来记录生成这个class文件的源码文件。

在这里插入图片描述
sourcefile_index数据项是指向常量池中的CONSTANT_Utf8_info型常量的索引,常量值是源码文件的文件名。

6. ConstantValue

该属性的作用是通知虚拟机自动为静态变量赋值。只有被static关键字修饰的变量才能使用这个属性,对于非static类型的变量的赋值是在<init>()方法中进行的,对于类变量,则有两种选择,首先是在类构造器<clinit>()方法中或者使用ConstantValue属性。如果同时使用final和static修饰一个变量,并且这个变量类型是基本类型或者java.lang.String类型就会使用ConstantValue进行初始化。否则用<clinit>()初始化。
在这里插入图片描述
7. InnerClasses属性

用于记录内部类和宿主类之间的关联。如果一个类中定义了内部类,那么编译器就会为它及内部类生成InnerClasses属性。InnerClasses属性结构如下:
在这里插入图片描述
number_of_classes代表该类的内部类数量,inner_classes_info用于描述内部类的信息,它也有自己的结构
在这里插入图片描述

  • inner_class_info_index:表示指向常量池中CONSTATN_Class_info型变量的索引,代表内部类的引用(外部类对内部类的引用)
  • inner_class_info_index:内部类对外部类的引用
  • inner_class_access_flag:内部类的访问标志
  • inner_name_index:指向常量池中CONSTANT_Utf8_info型常量的索引,代表这个内部类的名称,匿名内部类,这项值为0

8. Deprecated及Synthetic属性

Deprecated和Synthetic这两个属性都是布尔属性,前者表示某个类、字段或者方法,是否已经被作者指定为不再推荐使用,使用@deprecated注解标记。后者表示这个方法并不是由Java代码产生,而是由编译器自动添加的,在JDK5之后,标识一个类,字段或者方法是编译器自动产生的,也可以设置访问标记中的ACC_SYNTHETIC标志。编译器通过生成一些在源代码中不存在的synthetic方法,字段或者整个类的方式,实现了越权访问(越过private修饰器)或者其他语法限制功能,例如枚举类中自动生成的枚举元素数组和嵌套类的桥接方法。

9. StackMapTable属性

是JDK 6增加到Class文件规范中的,它是一个相当复杂的变长属性,位于Code属性表中。这个属性会在虚拟机加载的字节码验证阶段被新类型检查验证器使用,目的在于代替以前比较消耗性能的基于数据流分析的类型推导验证器。StackMapTable属性中包含0到多个栈映射帧,每个栈映射帧都显式或者隐式地代表一个字节码偏移量,用于表示执行到该字节码时局部变量表和操作数栈的验证类型。类型检查验证器会通过检查目标方法的局部变量和操作数栈所需要的类型确定一段字节码指令是否符合逻辑约束。
在这里插入图片描述
10.Signature属性

是JDK 5增加到Class规范中的,它是一个可选的定长属性,可以出现在类、字段表和方法表结构的属性表中。在JDK5中大幅增强类Java语言的语法,在此之后,任何类、接口、初始化方法或成员的泛型签名如果包含类型变量或参数化类型,则Signature属性会为它记录泛型签名信息。之所以要专门设置这一属性取记录泛型类型,是因为Java语言的泛型是采用的擦除法实现的伪泛型,字节码(Code)中所有的泛型信息编译(类型变量、参数化类型)在编译期都会擦除。这种方式就是实现简单,但是缺点也很明显,就是无法在运行期像C#一样真泛型支持的语言那样,将泛型类型当成用户定义的普通类型一样看待,例如运行期的反射时无法获取泛型信息,Signature属性的出现就是为了解决这个问题,限制Java的反射API能够获取泛型类型,最终的类型也是来源于这个属性。

在这里插入图片描述
其中signature_index项是一个对常池的有效索引,常量池在该索引处必须是CONSTANT_Utf8_info结构,表示类签名或方法签名或字段签名。如果当前的Signature属性是类文件属性,则这个结构表示类签名,如果是方法表属性就是方法类型签名,如果是字段表属性,就是字段类型签名。

11. BootstrapMethods属性

在JDK 17增加到Class文件规范之中,它是一个复杂的变长属性,位于类文件的属性表中。这个属性用于保存invokedynamic指令引用的引导方法限定符。如果某个类文件结构的常量池中曾经出现过 CONSTANT_InvokeDynamic_info类型的常量,那么这个类文件的属性表中必须存在一个明确的BootstrapMethods属性,另外,即使CONSTANT_InvokeDynamic_info 类型的常量在常量池中出现多次,类文件的属性表中最多也只能有一个BootstrapMethos属性。BootstrapMethos属性和JSR-292中的InvokeDynamic指令和java.lang.Invoke包关系非常密切,要介绍这个属性的功能我们首先需要知道InvokeDynamic指令的运作原理。虽然JDK 7中已经提供类InvokeDynamic指令,但这个版本的Javac编译器还暂时无法支持InvokeDynamic指令和生成BootstrapMethods属性,必须通过一些非常规的手段才能使用它们,知道JDK8中Lambda表达式和接口默认方法的出现,InvokeDynamic指令才算在Java语言生成的Class文件中有了用武之地。了
其中引用到了boostrap_method结构如下所示:

在这里插入图片描述
BootstrapMethods属性里,num_boostrap_methods项的值给出来boostrap_methods[]数组中的引导方法限定符的数量。而bootstrap_methods[]数组的每个成员包含一个指向常量池CONSTANT_MethodHandle结构的索引值,它代表一个引导方法。还包含了这个引导方法静态参数的序列(可能为空)。boostrap_methods[]数组的每个成员必须包含下三项内容:

  • boostrap_method_ref:必须是一个对常量池的有效索引。常量池在该索引处的值是一个CONSTANT_MethodHandle_info结构
  • num_boostrap_arguments:num_boostrap_arguments项的值给出了boostrap_arguments[]数组成员数量
  • boostrap_arguments[]:bootstrap_arguments[]数组的每个成员必须是一个对常量池的有效索引。常量池在该索引出必须下列结构之一:CONSTANT_String_info、CONSTANT_Class_info、CONSTANT_Integer_info、CONSTANT_Long_info、CONSTANT_Float_info、CONSTANT_Double_info、CONSTANT_MethodHandle_info、CONSTANT_MethodType_info

12. MethodParameter属性

MethodParameters是JDK 8时新加入到Class文件格式中,它是一个用在方法表中的变长属性。MethodParameters的作用是记录方法的各个参数名称和信息。它是方法表属性,与Code属性平级的,可以运行时反射API获取。

在这里插入图片描述
其中,name_index时一个指向常量池CONSTANT_Utf8_info常量的索引值,代表了参数的名称。而access_flagsf时参数的状态指示器,它可以包含以下三种状态中的一种或多种:

  • 0x0010(ACC_FINAL):表示该参数被final修饰
  • 0x1000(ACC_SYNTHETIC):表示该参数并未出现在源文件中,是编译器自动生成的
  • 0x8000(ACC_MANDATED):表示该参数是在源文件中隐式定义的,Java语言中典型的场景是this关键字

13. 运行时注解相关属性

早在JDK 5时期,Java语言的语法进行了多项增强,其中之一就是提供了对注解的支持。为例存储源码中的注解信息,Class文件同步增加了RuntimeVisibleAnnotations、RuntimeInvisibleAnnotations、RuntimeVisibleParameterAnnotations和RuntimeInvisibleParameterAnnotations四个属性。到了JDK 8时期,进一步加强了Java语言的注解使用范围,又增类型注解,所以Class文件中同步增加了RuntimeVisibleTypeAnnotations和RuntimeInvisibleTypeAnnotations两个属性,由于这六个属性不论结构还是功能都必须雷同,因此我们将它们合并在一起,以RuntimeVisibleAnnotations为代表进行介绍。RuntimeVisibleAnnotations是一个变长属性,它记录类、字段和方法的上面上记录运行时可见的注解,当我们使用反射API来获取类、字段或方法上的注解时,返回值就是通过这个属性来取到的。RuntimeVisibleAnnotations属性的结构如下:
在这里插入图片描述
type_index是一个指向常量池CONSTANT_Utf8_info常量索引值,该常量描述符的形式表示一个注解,num_element_value_pairs时element_value_pairs数组的计数器,elements_value_pairs中每个元素都是一个键值对,代表该注解的参数和值。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/506903.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

B端管理系统:UI设计师为什么没有话语权?

一、六大因素&#xff0c;导致了UI设计师话语权缺失。 专业性差异&#xff1a; UI设计师主要负责界面设计和用户体验&#xff0c;而在B端管理系统中&#xff0c;功能性和操作流程往往更为重要&#xff0c;需要产品经理和开发人员更多的参与&#xff0c;他们对于系统的功能和技…

Springboot Thymeleaf 实现数据添加、修改、查询、删除

1、引言 在Spring Boot中使用Thymeleaf模板引擎实现数据的添加、修改、查询和删除功能&#xff0c;通常步骤如下&#xff1a; 在Controller类中&#xff0c;定义处理HTTP请求的方法。创建Thymeleaf模板来处理表单的显示和数据的绑定。 2、用户数据添加 1、 在Controller类中…

尚医通day1

1 创建项目 doc 窗口 pnpm create vite 填写项目名 vue-syt选择框架 vuetypeScript 2整理项目 删除 /src/assets/vue.svg 文件&#xff0c;删除 /src/components 下的 helloWorld.vue删除app.vue内容&#xff0c;快捷键v3ts 生成模板内容去掉 /src/style.css 样式文件&…

格雷希尔G10系列L150A和L200A气动快速连接器,在新能源汽车线束线缆剥线后的气密性测试密封方案

线束线缆在很多用电环境都有使用&#xff0c;比如说新能源汽车&#xff0c;从电池包放电开始&#xff0c;高低压、通讯都开始进行工作&#xff0c;线束在连接的地方需要具有较高的气密性和稳定性&#xff0c;才能保证车辆在不同环境下能够正常的运行。 线束在组装铜鼻子前需要剥…

【Linux】开始掌握进程控制吧!

送给大家一句话&#xff1a; 我并不期待人生可以一直过得很顺利&#xff0c;但我希望碰到人生难关的时候&#xff0c;自己可以是它的对手。—— 加缪 开始学习进程控制 1 前言2 进程创建2.1 fork函数初识2.2 fork函数返回值2.3 写时拷贝2.4 fork常规用法2.5 fork调用失败的原因…

高阶DS---AVL树详解(每步配图)

目录 前言&#xff1a; AVL树的概念: AVL树节点的定义&#xff1a; AVL树的插入&#xff08;重点&#xff09; AVL树的旋转&#xff1a; &#xff08;1&#xff09;新节点插入较高左子树的左侧---右单旋 &#xff08;2&#xff09;新节点插入较高右子树的右侧---左单旋 …

(九)Docker的认识

1.初识Docker 1.1.什么是Docker 微服务虽然具备各种各样的优势&#xff0c;但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&#xff0c;依赖的组件非常多&#xff0c;不同组件之间部署时往往会产生一些冲突。在数百上千台服务中重复部署&#xff0c;环境不一定一致…

【更新】单细胞联合MR这样筛选靶点|衰老+NK细胞+免疫浸润

今天给大家分享一篇JCR一区&#xff0c;单细胞MR的文章&#xff1a;Unraveling the mechanisms of NK cell dysfunction in aging and Alzheimer’s disease: insights from GWAS and single-cell transcriptomics 标题&#xff1a;揭示NK细胞在衰老和阿尔茨海默病中功能失调的…

Pangolin_FOUND to FALSE so package “Pangolin“ is considered to be NOT FOUND.

修改CMakeLists.txt如下&#xff1a; 在find_package(Eigen3 REQUIRED)后加NO_MUDULE, find_package(Eigen3 REQUIRED NO_MODULE)编译成功&#xff1a;

挖一挖:PostgreSQL Java里的double类型存储到varchar精度丢失问题

前言 大概故事是这样的&#xff0c;PostgreSQL数据库&#xff0c;表结构&#xff1a; create table t1(a varchar);然后使用标准的Java jdbc去插入数据&#xff0c;其基本代码如下&#xff1a; import java.sql.*; public class PgDoubleTest {public static void main(Stri…

Bridge Champ与Ignis公链:探索Web3游戏的新未来

在数字化和去中心化的浪潮中&#xff0c;Web3游戏与公链的融合为游戏行业带来了新的变革。特别是&#xff0c;Bridge Champ和Ignis公链的结合&#xff0c;展示了一种全新的游戏生态模式&#xff0c;不仅为玩家提供了更加公平、透明的游戏体验&#xff0c;同时也为游戏开发和运营…

狐臭的等比数列

题目 #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e9, maxm 4e4 5; const int N 1e6;co…

乡村智慧化:数字乡村助力农村可持续发展

目录 一、数字乡村的内涵与特征 二、数字乡村助力农村可持续发展的路径 &#xff08;一&#xff09;提升农业生产效率 &#xff08;二&#xff09;推动农村产业融合发展 &#xff08;三&#xff09;优化乡村治理模式 &#xff08;四&#xff09;促进乡村生态文明建设 三…

基于ssm校园活动管理平台论文

摘 要 使用旧方法对校园活动信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在校园活动信息的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。 这次开发的校园活动管理平…

Python学习笔记-Flask接口创建与测试

服务端: 1.引包 导入 from flask import Flask, request, render_template, redirect Flask: Flask 是一个类&#xff0c;用于创建 Flask web 应用的实例。每个 Flask 应用都从创建这个类的实例开始。示例&#xff1a;app Flask(__name__) request: request 是一个全局对象…

【BlossomConfig】什么是配置中心?以及如何实现一个配置中心?

文章目录 什么是配置中心&#xff1f;如何自己设计一个配置中心&#xff1f; 网关项目源码 RPC项目源码 配置中心项目源码 什么是配置中心&#xff1f; 在单体架构的时候我们可以将配置写在配置文件中&#xff0c;但有⼀个缺点就是每次修改配置都需要重启服务才能生效。 当应用…

MySQL使用技巧,高级Java开发必看

insert into tab(col1,col2…) select … 5、活用正则表达式 regexp ^ $ . * | 6、关联查询比子查询效率快&#xff0c;优先使用join关联查询 7、if(exp,v1,v2) if()函数的使用 exp:表达式 v1:exp为真时返回的值 v2:exp为假时返回的值 8、case when… then… else… en…

D34118电话机免提通话电路应用方案

1、 概述&#xff1a; D34118免提语音通话电路包含了必要的放大器、衰减器、背景噪声检测和控制算法形成高品质的免提通话系统。它包括一个麦克风可调增益放大器、静音控制、发射和接收衰减器&#xff0c;还包括两个线路驱动放大器&#xff0c;可用于形成一个与外部耦合变压器连…

【虚幻引擎】C++ slate全流程开发教程

本套课程介绍了使用我们的虚幻C去开发我们的编辑器&#xff0c;扩展我们的编辑器&#xff0c;设置我们自定义样式&#xff0c;Slate架构设计&#xff0c;自定义我们的编辑器样式&#xff0c;从基础的Slate控件到我们的布局&#xff0c;一步步的讲解我们的的Slate基础知识&#…

Python 人工智能实战|产生式规则推理系统:动物识别系统、智能客服系统

一、动物识别系统 1.1&#xff1a;前言 产生式系统是基于产生式规则的推理系统&#xff0c;它以产生式规则为基础&#xff0c;利用规则匹配的推理机制来进行推断和解决问题。下图是产生式系统的组成&#xff0c;请从“动物识别系统”程序代码中分别找出规则库、综合数据库和推…