简述

class文件是什么?稍微接触过java的同学都会说:就是java源文件编译后的.class文件。这句话虽然对,但是不够准确。其他语言,例如scala,jruby等,都可以编译出class文件。class文件是能被jvm识别和运行的一种文件格式。它是八位的二进制流文件。今天就跟大家一起学习class文件的格式

整体结构

用ultraedit等工具打开class文件,看到的都是二进制。首先要了解哪几位代表什么标志?这里用u1,u2,u4,u8,代表属性占用多少个字节。

格式,如下
类型 名称 数量
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 interface_count
u2 fields_count 1
field_info fields fields_count
u2 methods_count 1
method_info methods methods_count
u2 attributes_count 1
attributes_inof attributes attributes_count

详细
看了上面的大体介绍,明白了class文件的基本结构后,我们就逐一来介绍各个部分。

1.magic

前四个字节 0xCAFEBABE,就是代表该文件格式是class文件了。出于安全考虑,class文件不是以文件后缀名来识别

2.minor_version 和 major_version

jvm只能处理低于或者等于它版本后的class文件。所以class文件首先要定义自己的版本,才能让jvm判断能否执行。版本号的描述一般都是 major_version.minor_version。各个版本号代表的数值可以去查看jvm说明书

3.constant_pool

常量池,里面包含了类和接口相关的常量,比如final变量值、类名、方法名等。接下来定义方法等信息时,就给一个索引去常量池寻找相关的信息。这个索引是从1开始的。为什么不是0开始?其实是有的,只不过被当做特殊用途了。所以constant_pool_count是将索引0也计入在内。每个常量值都有一个u1大小的标志。如下:

入口类型 标志值 描述
CONSTANT_Utf8 1 UTF-8编码的Unicode字符串
CONSTANT_Integer 3 int类型字面量
CONSTANT_Float 4 float类型字面值
CONSTANT_Long 5 long类型字面量
CONSTANT_Double 6 double类型字面量
CONSTANT_Class 7 对一个类或者接口的符号引用
CONSTANT_String 8 String类型字面量
CONSTANT_Fieldref 9 对一个字段的符号引用
CONSTANT_Methodref 10 对一个类中声明的方法的符号引用
CONSTANT_InterfaceMethodRef 11 对一个接口中声明的方法的符号引用
CONSTANT_NameAndType 12 对一小字段或者发放的部分符号引用

常量池中包括了:字面量;类和接口的全限定名;字段的名称和描述符;方法的名称和描述符。字段飞描述符就是字段类型的字符串。方法的描述符是方法返回值和参数的数量,顺利和类型的描述。jvm只是从常量池中获得符号引用,然后在运行时解析引用项的实际地址。

4.access_flags

顾名思义就是类或者接口的访问修饰符,例如public abstract等。每个访问修饰符用一位二进制位来表示。access_flags就是所有的访问修饰符“与”关系。//todo 访问修饰符的定义

5.this_class

类的基本信息。包括两个字节,是常量池的一个CONSTANT_Class_info表的索引。该表结构包括两部分:tag和name_index。tag值等于该索引值。通过name_index,我们又可以在常量池中找到一个对应的CONSTANT_Utf8_info表。Utf8表结构包括三部分:tag(等于utf8表的索引值);length和byte(字符串内容)

6.super_class

父类的相关信息。格式跟this_class一样。每个对象只有一个父类(单继承)。而object类的super_class为0

7.interfaces_count和interface

所实现的接口的相关信息。每个接口都由一个索引指向常量池中的一个CONSTANT_Class_info

8.field_count和fields

字段的相关信息。每个字段都由一个索引指向常量池的一个field_info。这里不会出现父类的field。但可能出现一些代码中没有定义的字段,是编译器额外添加的。例如,内部类中,会有一个外部类的实例

9.methods_count和methods

关于方法的相关信息,每个方法都有一个索引指向常量池的method_info表

10.attributes_count和attributes

类或接口所定义的属性的基本信息。每个属性都由一个索引指向常量池的attribute_info。属性在很多地方都出现,比如在field_info中出现描述与字段相关的属性信息。在顶层ClassFile中有两种属性-SourceCode和InnerClass

11.几个常用到的概念

全限定名-把类的全名用.替换成/

简单名称-方法或者字段的名称,不包括任何的修饰符

描述符号
比较复杂.先来看类型的定义.B-byte,C-char,D-double,F-float,I-int,J-long,S-short,Z-boolean,V-void,L对象全限定名-对象类型.
描述数组,用前置的”[“来表示,有几维就加多少个”[“.

字段描述符=数组描述符+类型描述符
方法描述符=左括号+参数描述符(按定义的顺序)+右括号+返回值描述符

总结:

跟大家一起稍微了解了class文件的结构,明白了它的大概组织结构。篇幅有限,本来应该找个最简单的helloworld程序的字节码来一一说明的。详细例子,大家可以参考以下书籍

参考资料

1.深入java虚拟机
2.深入理解java虚拟机