每个class文件对应一个如下所示的ClassFile结构体
ClassFile {
u4 magic; #魔数
u2 minor_version; #class文件副版本号,JDK版本
u2 major_version; #class文件主版本号,JDK版本
u2 constant_pool_count; #常量池数量
cp_info constant_pool[constant_pool_count-1]; #常量池
u2 access_flags; #访问标志
u2 this_class; #类索引,对应constant_pool表中的一个有效索引值
u2 super_class; #父类索引
u2 interfaces_count; #接口计数器
u2 interfaces[interfaces_count]; #接口表
u2 fields_count; #字段计数器
field_info fields[fields_count]; #字段表
u2 methods_count; #方法计数器
method_info methods[methods_count]; #方法表
u2 attributes_count; #属性技术器
attribute_info attributes[attributes_count]; #属性表
}
一、魔数
魔数的唯一作用是标记这个文件是否是一个可被虚拟机识别的class文件,魔数固定值为0xCAFEBABE,不会改变。(传说好像java一直为咖啡代言哈... ...)。
二、主副版本号
major_version、minor_version,共同构建了class文件格式版本号,eg:major_version=M,minor_version=m,那么该class文件格式版本号为M.m。
一个java虚拟机实例只能支持特定范围内的主版本号(Mi至Mj)和0至特定范围的副版本号,假设一个class文件的格式版本号为V,仅当Mi.0<v<Mj.m成立时,这个class文件才可以被此java虚拟机支持。高版本java虚拟机可以支持低版本class文件,反之不行。以下JDK版本对应到1.8,更高的以此类推即可。
JDK版本号 | Class版本号 | 16进制 |
---|---|---|
1.1 | 45.0 | 00 00 00 2D |
1.2 | 46.0 | 00 00 00 2E |
1.3 | 47.0 | 00 00 00 2F |
1.4 | 48.0 | 00 00 00 30 |
1.5 | 49.0 | 00 00 00 31 |
1.6 | 50.0 | 00 00 00 32 |
1.7 | 51.0 | 00 00 00 33 |
1.8 | 52.0 | 00 00 00 34 |
三、常量池计数器
constant_pool_count等于constant_pool表中的成员数加1(想想为什么+1,段位最后一句为解释)。constant_pool表的索引值只有在大于0且小于constant_pool_count时才会被认为是有效的,对于long合double类型有列外的情况。值为0的constant_pool索引是无效的,当其他数据结构用到0的索引来表示“不引用任何一个常量池项”的意思。
四、常量池
constant_pool是一种表结构,它包含Class文件结构及其子结构中引用的所有字符串常量、类或接口名、字段名和其他常量。常量池中的每一项都具备相同的格式特征——第一个字节作为类型标记用于识别该项是哪种类型的常量,称为“tag byte”。常量池的索引范围是1至constant_pool_count-1.格式如下
cp_info {
u1 tag;
u1 info[];
}
tag项说明如下:
常量类型 | 值 |
---|---|
CONSTANT_Class | 7 |
CONSTANT_Fieldref | 9 |
CONSTANT_Methodref | 10 |
CONSTANT_InterfaceMethodref | 11 |
CONSTANT_String | 8 |
CONSTANT_Integer | 3 |
CONSTANT_Float | 4 |
CONSTANT_Long | 5 |
CONSTANT_Double | 6 |
CONSTANT_NameAndType | 12 |
CONSTANT_Utf8 | 1 |
CONSTANT_MethodHandle | 15 |
CONSTANT_MethodType | 16 |
CONSTANT_InvokeDynamic | 18 |
以上tag中项的格式说明详见:constant_pool格式详解
五、访问标识
access_flags是一种掩码标志,用于表示某个类或者接口的访问权限及基础属性。eg:0x0021=0x0020 | 0x0001->ACC_PUBLIC | ACC_SUPER
标志名 | 标志值 | 标志含义 | 针对的对像 |
---|---|---|---|
ACC_PUBLIC | 0x0001 | public类型 | 所有类型 |
ACC_FINAL | 0x0010 | final类型 | 类 |
ACC_PROTECTED | 0x0004 | protected; 可以在子类中访问 | |
ACC_SUPER | 0x0020 | 使用新的invokespecial语义 | 类和接口 |
ACC_INTERFACE | 0x0200 | 接口类型 | 接口 |
ACC_ABSTRACT | 0x0400 | 抽象类型 | 类和接口 |
ACC_SYNTHETIC | 0x1000 | 该类不由用户代码生成 | 所有类型 |
ACC_ANNOTATION | 0x2000 | 注解类型 | 注解 |
ACC_ENUM | 0x4000 | 枚举类型 | 枚举 |
ACC_STATIC | 0x0008 | 申明为static | 所有 |
ACC_VOLATILE | 0x0040 | volatile; 不能被缓存. | |
ACC_TRANSIENT | 0x0080 | transient; 持久化对象管理器不会读和写. |
ACC_SYNTHETIC:由编译器自己产生的而不是由程序员编写的源代码生成的,以下为该类型的解释。
package com.weyne.demo.jvm;
public class SyntheticDemo {
public static void main(String[] args) {
InnerClassDemo innerClassDemo = new InnerClassDemo();
System.out.println(innerClassDemo.hashCode());
}
private static class InnerClassDemo{
private String a = "私有类部类";
}
}
该类编译后在本地生成有SyntheticDemo$1.class、SyntheticDemo$InnerClassDemo.class、SyntheticDemo.class,其中SyntheticDemo$1.class就为ACC_SYNTHETIC类型,javap -v XXX.class查看class结构
六、类索引
this_class,值为constant_pool中的一个有效索引值,且constant_pool中的该项为CONSTANT_Class_info类型常量,表示这个class文件所定义的类或接口。
七、父类索引
super_class,对于类而言,该值必须为0或者对应constant_pool表中的一个有效索引值,如果不为0,那constant_pool表在这个索引处的项必须为CONSTANT_Class_info类型常量,表示这个Class文件所定义的类的直接父类。当前类的直接父类以及他所有的间接父类的access_flag中都不能带有ACC_FINAL。如果值为0,则该类可能为java.lang.Object,只有它是唯一没有父类的类。
八、接口计数器
interface_count,表示当前类或接口的直接父接口数量.
九、接口表
interfaces[],长度为interface_count,值对应constant_pool中的有效索引值,成员必须为CONSTANT_Class_info。成员所表示的接口顺序和对应的源代码中给定的接口顺序(从左至右)一样。
10、字段计数器
field_count,表示当前Class文件fields[]数组的成员个数,fields[]数组中的每一项都是field_info接口,用于表示该类或接口声明的类字段或者实例字段。类字段即被声明为static的字段,也称类变量或类属性,实例字段是指未被声明为static的字段。