Class文件格式
类型 | 名称 | 数量 |
---|---|---|
u4 | magic | 1 |
u2 | minor_version | 1 |
u2 | major_version | 1 |
u2 | constant_pool_count | 1 |
cp_info | constant_pool | constant_pool_count |
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 | 1 |
u2 | methods_count | 1 |
method_info | methods | methods_count |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
魔数与 Class文件的版本
魔数(magic):ca fe ba be
此版本(minor_version):00 00
主版本(major_version):00 34
常量池
常量池数据(constant_pool_count):0x16=22
,由于第0个数字有其他含义,所有只有21
个,就代表常量池中有21项常量,索引值范围为1~21。
常量池中主要存放两大类常量:字面量( Literal)和符号引用( Symbolic References)。字面量比较接近于Java语言层面的常量概念,如文本字符串、声明为fnal的常量等。而符号引用则属于编译原理方面的概念,包括了下面三类常量:
- 类和接口的全限定名( Fully Qualified Name)
- 字段的名称和描述符( Descriptor)
- 方法的名称和描述符
常量池的项目类型
类型 | 标志 | 描述 |
---|---|---|
CONSTANT_utf8_info | 1 | UTF-8编码的字符串 |
CONSTANT_Integer_info | 3 | 整形字面量 |
CONSTANT_Float_info | 4 | 浮点型字面量 |
CONSTANT_Long_info | 5 | 长整型字面量 |
CONSTANT_Double_info | 6 | 双精度浮点型字面量 |
CONSTANT_Class_info | 7 | 类或接口的符号引用 |
CONSTANT_String_info | 8 | 字符串类型字面量 |
CONSTANT_Fieldref_info | 9 | 字段的符号引用 |
CONSTANT_Methodref_info | 10 | 类中方法的符号引用 |
CONSTANT_InterfaceMethodref_info | 11 | 接口中方法的符号引用 |
CONSTANT_NameAndType_info | 12 | 字段或方法的符号引用 |
CONSTANT_MothodType_info | 15 | 标志方法类型 |
CONSTANT_MethodHandle_info | 16 | 表示方法句柄 |
CONSTANT_InvokeDynamic_info | 18 | 表示一个动态方法调用点 |
源文件
package com.test;
public class TestClass {
private int m;
public int getM() {
return m + 2;
}
}
使用 Javap命令输出常量表
javap -v class文件
javap -v com.test.TestClass
Classfile /C:/temp-workspace/ericsson-workspace/FCAPS_CM/target/classes/com/test/TestClass.class
Last modified 2019-11-27; size 372 bytes
MD5 checksum 523f379513308882e93adadf7024c6bb
Compiled from "TestClass.java"
public class com.test.TestClass
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #4.#18 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#19 // com/test/TestClass.m:I
#3 = Class #20 // com/test/TestClass
#4 = Class #21 // java/lang/Object
#5 = Utf8 m
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/test/TestClass;
#14 = Utf8 getM
#15 = Utf8 ()I
#16 = Utf8 SourceFile
#17 = Utf8 TestClass.java
#18 = NameAndType #7:#8 // "<init>":()V
#19 = NameAndType #5:#6 // m:I
#20 = Utf8 com/test/TestClass
#21 = Utf8 java/lang/Object
{
public com.test.TestClass();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 15: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/test/TestClass;
public int getM();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: getfield #2 // Field m:I
4: iconst_2
5: iadd
6: ireturn
LineNumberTable:
line 19: 0
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this Lcom/test/TestClass;
}
SourceFile: "TestClass.java"
常量池中的14种常量项的结构总表
常量 | 项目 | 类型 | 描述 |
---|---|---|---|
CONSTANT_Utf8_info | tag | u1 | 值为1 |
length | u2 | UF-8编码的字符串占用的字节数 | |
bytes | u1 | 长度为length的UTF-8编码的字符串 | |
CONSTANT_Integer_info | tag | u1 | 值为3 |
bytes | u4 | 按照高位在前存储的int值 | |
CONSTANT_Float_info | tag | u1 | 值为4 |
bytes | u4 | 按照高位在前存储的float值 | |
CONSTANT_Long_info | tag | u1 | 值为5 |
bytes | u8 | 按照高位在前存储的long值 | |
CONSTANT_Double_info | tag | u1 | 值为6 |
bytes | u8 | 按照高位在前存储的double值 | |
CONSTANT_Class_info | tag | u1 | 值为7 |
index | u2 | 指向全限定名常量项的索引 | |
CONSTANT_String_info | tag | u1 | 值为8 |
index | u2 | 指向字符串字面量的索引 | |
CONSTANT_Fieldref_info | tag | u1 | 值为9 |
index | u2 | 指向声明字段的类或接口描述符CONSTANT_Class_info的索引项 | |
index | u2 | 指向字段名称及类型描述符CONSTANT_NameAndType_info的索引项 | |
CONSTANT_Methodref_info | tag | u1 | 值为10 |
index | u2 | 指向声明方法的类描述符CONSTANT_Class_info的索引项 | |
index | u2 | 指向方法名称及类型描述符CONSTANT_NameAndType_info的索引项 | |
CONSTANT_InrerfaceMethodref_info | tag | u1 | 值为11 |
index | u2 | 指向声明方法的接口描述符CONSTANT_Class_info的索引项 | |
index | u2 | 指向方法名称及类型描述符CONSTANT_NameAndType_info的索引项 | |
CONSTANT_NameAndType_info | tag | u1 | 值为12 |
index | u2 | 指向字段或方法名称常量项目的索引 | |
index | u2 | 指向该字段或方法描述符常量项的索引 | |
CONSTANT_Method-Handle_info | tag | u1 | 值为15 |
reference_kind | u1 | [1,9],决定了方法句柄的类型。方法句柄类型的值表示方法句柄的字节码行为 | |
reference_index | u2 | 值必须为常量池的有效索引 | |
CONSTANT_Method-Type_info | tag | u1 | 值为16 |
descriptor_index | u2 | 值必须为常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示方法的描述符。 | |
CONSTANT_Invoke-Dynamic_info | tag | u1 | 值为18 |
bootstrap_method_attr_index | u2 | 值必须是对当前Class文件中引导方法表的bootstrap_methods[]数组的有效索引 | |
name_and_type_index | u2 | 值必须是对当前常量池的有效索引,常量池在该索引处的项必须是CONSTANT_NameAndType_info结构,表示方法名和方法描述符 |
访问标志
类型 | 名称 | 数量 |
---|---|---|
u2 | access_flags | 1 |
在常量池结束之后,紧接着的两个字节代表访问标志( access flags),这个标志用于识别一些类或者接口层次的访问信息,包括:这个Cas是类还是接口;是否定义为 public类型;是否定义为 abstract类型;如果是类的话,是否被声明为 final等。
标志名称 | 标志值 | 含义 |
---|---|---|
ACC PUBLIC | 0x0001 | 是否为 public类型 |
ACC FINAL | 0x0010 | 是否被声明为 final,只有类可设置 |
ACC SUPER | 0x0020 | 是否允许使用 invokespecial字节码指令的新语意, invokespecial指令的语意在JDK1.0.2发生过改变,为了区别这条指令使用哪种语意,JDK1.0.2之后编译出来的类的这个标志都必须为真 |
ACC INTERFACE | 0x0200 | 标识这是一个接口 |
ACC ABSTRACT | 0x0400 | 是否为 abstract类型,对于接口或者抽象类来说,此标志值为真,其他类值为假 |
ACC SYNTHETIC | 0x1000 | 标识这个类并非由用户代码产生的 |
ACC ANNOTATION | 0x2000 | 标识这是一个注解 |
ACC ENUM | 0x4000 | 标识这是一个枚举 |
类索引、父类索引与接口索引集合
类型 | 名称 | 数量 |
---|---|---|
u2 | this_class | 1 |
u2 | super_class | 1 |
u2 | Interfaces_count | 1 |
类索引( this class)和父类索引( super class)都一个u2类型的数据,而接口索引集合( interfaces)是一组u2类型的数据的集合, Class文件中由这三项数据来确定这个类的继承关系。类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名由于Java语言不允许多重继承,所以父类索引只有一个,除了 java. lang. Object之外,所有的Java类都有父类,因此除了 java. lang Object外,所有Java类的父类索引都不为0。接口索引集合就用来描述这个类实现了哪些接口,这些被实现的接口将按 implements语句(如果这个类本身是一个接口,则应当是 extends语句)后的接口顺序从左到右排列在接口索引集合中
类索引、父类索引和接曰索引集合都按顺序排列在访问标志之后,类索引和父类索引用两个u2类型的索引值表示,它们各自指向一个类型为 coNSTANT Class info的类描述符常量,通过CONSTANT_Class_info类型的常量中的索引值可以找到定义在CONSTANT_Utf8_info类型的常量中的全限定名字符串。