文章目录
- JAVA的平台无关性怎么实现的?
- JAVA的类文件结构?
- 魔数与版本
- 常量池
- 访问标志
- 类索引父类索引与接口索引集合
- 字段表集合
- 方法表集合
- 属性表集合
- Class文件的基础单位是什么?
- 方法里的JAVA代码编译成字节码后存放在哪里?
- 子类方法表会不会有父类的方法?
- 参考资料
JAVA的平台无关性怎么实现的?
字节码是构成平台无关系的基石。JAVA在刚刚诞生时就提出"Write Once, Run Anywhere",将编写的程序编译成所有平台都可以使用的程序存储格式:字节码。JAVA虚拟机不和任何语言绑定,它只与"Class文件"这种二进制文件格式相关联。任意一门功能性语言都可以把程序代码编译成Class文件被JAVA虚拟机接受,如Scala、Clojure、Groovy、JRuby、Jython等。
JAVA的类文件结构?
Class文件格式采用一种伪结构来存储数据,包括两种数据类型:无符号数和表。整个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 |
从上到下分析:
魔数与版本
开头4个字节(u4)称为魔数(Magic Number),它唯一作用是表明这是一个能被虚拟机接受的Class文件。这四个字节用16进制来表示是0xCAFEBABE。
紧接着的u2两个字节是次版本号,再后面u2两个字节是主版本号。
常量池
由于常量池中常量的数量是不固定的,所以先用u2标明容量(constant_pool_count) 。如果是0x0016,则表明有21个常量(1*16+6-1)。 只有常量池的容量是从1开始计数的,其他集合类型都是从0开始计数。
常量池存放两大类常量:字面量和符号引用。字面量如文本字符串、声明为final的常量值等。符号引用包括:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。符号引用不经过运行期转换无法转为真正的内存地址。每个常量都有一个tag位(u1)来标明常量的类型,如整型字面量、浮点型字面量、类和接口的符号引用、字段或方法的符号引用等。可用javap命令来输出Class文件的字节码。
javap -verbose TestClass
访问标志
access_flags为两个字节,标明是否为类或接口,是否为public、abstract、final等。
类索引父类索引与接口索引集合
this_class(类索引)、super_class(父类索引)各自指向一个类型为CONSTANT_Class_info的类描述符常量,通过其索引值name_index可以找到常量池中的全限定名。
字段表集合
字段表(field_info)包括类和接口中声明的变量。
变量会由各种修饰符来定义:public/private/protected、static、final、volatile、transient、int/long/float、是数组还是对象等等。
每个字段由access_flags、name_index、descriptor_index来表示,即字段修饰符、简单名称索引和描述符索引。简单名称可理解为变量名,实际值存在常量池中,这里只维护引用。描述符用大写字母表示:I代表int,B代表byte,J代表long,Z代表boolean、L代表对象类型等等。
方法表集合
方法表跟字段表相似,多了一个属性表集合。
属性表集合
在Class文件、字段表、方法表中都可以携带属性表。
如方法表中的Code、字段表中的ConstantValue、Code属性中的LineNumberTable、LocalVariableTable、StackMapTable。
属性的名称在常量池中,由attribute_name_index引用。
这里只介绍Code属性:除了接口或抽象类中的方法,其他的方法体编译后的字节码指令存储在Code属性内。包括属性名索引、属性长度、操作数栈最大深度、局部变量表所需空间、源代码生成的字节码指令、异常表等。
Class文件的基础单位是什么?
8位字节的二进制流。如果超出8位,高位在前(Big-Endian)。
方法里的JAVA代码编译成字节码后存放在哪里?
Code属性里面。
子类方法表会不会有父类的方法?
不会,除非子类重写(Override)了父类方法。
参考资料
《深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)》 周志明 著