类文件结构

代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言发展的一大步。

6.1 概述

实现语言无关性的基础仍然是虚拟机和字节码存储格式。Java虚拟机不与包括Java语言在内的任何程序语言绑定,它只与“Class文件”这种特定的二进制文件格式所关联,Class文件中包含了Java虚拟机指令集、符号表以及若干其他辅助信息。

6.3 class类文件的结构

Class文件是一组8个字节为基础单位的二进制流,中间没有分隔符。若需要存储8个字节以上的数据,则会按照高位在前的方式分隔成若干8个字节进行存储。

class文件采用类似结构体的伪结构来存储数据,其有两种数据类型:“无符号数”,“表”。

  • 无符号数属于基本数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串。

  • 表是由多个无符号数或者其他表作为数据项构成的复合数据类型,其用于描述有层次关系的复合结构的数据,整个Class文件本质上也可以视作是一张表,表结构如下所示。

    类型 名称 数量
    u4 magic 1
    u2 minor_version 1
    u2 major_version 1
    u2 constant_pool_count 1
    cp_info constant_pool constant_pool_count-1
    u2 access_flags 1
    u2 this_class 1
    u2 super_class 1
    u2 interfaces_count 1
    u2 interfaces interfaces_count
    u2 fields_count 1
    field_info fields fields_count
    u2 methods_count 1
    method_info methods methods_count
    u2 attributes_count 1
    attribute_info attributes attributes_count

6.3.1 魔数与Class文件的版本

魔数的作用,是确定这个文件是否为一个能被虚拟机接受的Class文件。

Minor Version:次版本号,Major Version:主版本号。

高版本JDK可以兼容低版本JDK,但是不能运行以后版本的Class文件。

6.3.2 常量池

常量池可以喻为Class文件里的资源仓库,也是Class文件中第一个出现的表类型数据项目。

常量池主要存放两类常量:

  • 字面量(Literal):文本字符串,被声明为final的常量
  • 符号引用(Symbolic References):被模块导出的或者开放的包(Package)、类和接口的全限定名(Fully Qualified Name)、字段的名称和描述符(Descriptor)、方法的名称和描述符、方法句柄和方法类型(Method Handle、Method Type、Invoke Dynamic)、动态调用点和动态常量(Dynamically-Computed Call Site、Dynamically-Computed Constant)

Java代码在进行Javac编译的时候,不会去做真正的连接,而是在虚拟机加载Class文件的时候进行动态连接。

既在class文件中不会保存各个方法、字段在内存中的布局信息,这些方法、字段的符号引用不经过虚拟机在运行期转换的话是无法得到真正的内存入口地址,也就无法被虚拟机真正使用。

当虚拟机在做类加载时,会从常量池中获得对应的符号引用,在类创建时或运行时解析、翻译到具体的内存地址之中。

6.3.3 访问标志

用于识别类或接口的访问信息,包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract类型;如果是类的话,是否被声明为final;等。

6.3.4 类索引、父类索引和接口索引集合

类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名。

6.3.5 字段表集合

其用于描述接口或类中声明的变量。Java中的“字段”包括类级变量(static)以及实例变量,但不包括在方法内部声明的局部变量。

字段的各种修饰符很适合用标志位来描述,但数据类型和名称因为长度不定故需要引用常量池中的常量来描述。

6.3.6 方法表集合

Class文件存储格式中对方法的描述与对字段的描述几乎一致,方法表包括:访问标志,名称索引,描述符索引,属性表等几项。

方法内的代码被编译后存于属性表一个名为“code”的属性里。

在Java中,要重载一个方法,除了方法名称相同之外,还需要有一个与原方法不同的特征签名。特征签名是指一个方法中各个参数在常量池中的字段符号引用的集合,正因为返回值不会包含在特征签名之中,所以Java是无法依靠返回值的不同来对一个已有方法进行重载的。

JVM中是如何描述方法的返回类型的?

6.3.7 属性表集合

pass

作者

bd160jbgm

发布于

2021-06-03

更新于

2021-06-03

许可协议