JAVA的class文件格式例解

本文详细解析了Java源代码编译后的字节码文件结构,包括类的访问标志符、常量池、访问者、字段、方法以及类的属性等关键元素。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JAVA程序从源代码到运行的一般过程为:
JAVA源文件编译之后形成class文件,也就是java字节码文件,然后java虚拟机解释执行
java字节码文件。我们来简单看看java字节码文件的格式。下图是java字节码文件的格式的定义。

u4代表占4个字节,cp_info表示cp_info结构需要占据的字节,后面会介绍cp_info结构。
这些count的最大长度意味着为0xFFFF=65535,如果超过,javac会造成“code too large”错误
假设有一个java文件的源代码如下

public class ClassFormatTest {
	public static String staticStr = "static";
	private int instanceInt = 1;
	private int[] intArray = { 1, 2, 3 };
	static{
		staticStr="changed in static block";
	}
	public int add(int c){
		int a = 10;
		int b = 23;
		int d = a + b;
		return d;		
	}
}
我们来看看编译成二进制文件后文件的内容,以16进制的形式打开,我们可以看到如下图所示:


ca fe ba be 为magic number,00 00 00 32 为此版本号和主版本号 0x32=50=jdk1.7
接下来是常量池的数量00 27=39,对应constant_pool_count,那么一共有39-1=38个常量项。
常量池是数组的形式。保存了类中用到的一些不变的"常量",如标识符public,private,
类名称,方法名,数据类型,字面值,变量名称等等。
接下来是第一个常量项,第一个字节07 是这个常量项的类型。
常量池中的16进制和类型的对应如下图所示:

根据常量池中的16进制和类型的对应关系图可以得知这是一个class的信息。
一个class的cp-info结构如下图所示:

00 02表示它的值为常量池数组的第二项。为const #2 = Asciz    ClassFormatTest;(参照后面javap反编译的结果)
字节码的项于项之间是没有空格的,接下来第二项01表示常量项type为utf8编码的字符串,
对应下图的utf8 string的cp-info结构,可以得知 00 0f是表示长度为15.
如上面二进制文件那副图中划线的内容,后面的15个字节的内容表示的字符串值为ClassFormatTest。

常量池一致持续到划椭圆的代表这个类的访问标志符 0x0021(public,jkd1.2之后)
16进制数和访问标识符对应关系如下图所示:

访问标识符(access flags)之后的00 01 00 03 00 00分别表示this class,supper class,
interfaces count,由于interfaces count是 0000所以interfaces没有字节表示。
接下来是fields的信息,如下图所示所示的0003这两个字节表示fields_count:


00 03表示fields 数量为3。
接下来是第一个字段的信息,参照下图中的字段结构对应关系,
接下来两个字节为access flag, 0x0009表示 public static,
再接下来两个字节为字段名,00 05指向常量池中的第5项,const #5 = Asciz    staticStr;(参照后面javap反编译的结果)
再接下来两个字节为字段类型,00 06指向常量池中的第6项,const #6 = Asciz    Ljava/lang/String;;
再接下来两个字节为字段属性个数,00 00表示没有attributes。
接下就是第二个字段的信息
0002为第二个field的access flag信息,表示 private。
00 07指向常量池中的第7项,const #7 = Asciz    instanceInt;
00 08指向常量池中的第8项,const #8 = Asciz    I;
00 00表示没有attributes。
其他字段类推...
字段结构对应关系如下图

methods_count字节位置的0003表示有3个方法,
接下来两个字节是第一个方法的访问标识符,0008表示为static
接下来两个字节为方法名,000b对应 const #11 = Asciz    <clinit>; (类构造器方法)
再接下来两个字节为方法描述,000c对应 const #12 = Asciz    ()V;
再接下来两个字节为属性个数,00 01表示有一个属性。
再接下来两个字节为属性值, 00 0d表示常连池的第13项const #13 = Asciz    Code;
下面是方法的结构关系图

接下来是方法代码的信息,根据下图code结构的对应关系分析:

我们来仔细分析一下add方法
划线的00 01 00 1f 00 20 00 01 00 0d中的
00 01是方法的访问标识符表示这是 public。
00 1f是方法的的名称add方法(const #31),
00 20表示descriptor为const #32 = Asciz    (I)I; 参数int,返回值为int。
00 01表示有一个属性,00 0d表示根据属性的index参照常量池中得#13项为代码。
接下来的4字节00 00 00 6c 表示属性的长度为 108。(这个长度包括code字节码,
exception,LineNumberTable和LocalVariableTable等)
在接下来的00 02 表示Stack=2,
00 05表示Locals=5,
接下来的4字节00 00 00 0e 表示code的长度为14。
接下来的14个字节为虚拟机的字节码指令,10 0a 3d 10 17 3e 1c 1d 60 36 04 15 04 ac
10 0a对应 bipush    10,3d 对应istore_2,10 17对应bipush    23,
3e对应istore_3,直到ac对应ireturn
两个字节合起来的话,后面的一个是操作数,有的指令是不需要操作数的则为一个字节。
接下来的0000表示exception_table_length为0.
再接下来的0002表示attributes_count为2.
再接下来的0014对应到常量池中的第20项,const #20 = Asciz    LineNumberTable;表示为LineNumberTable.

再接下来的00000012表示attributes_length 为18.
接下来为line_number_table_length,0004表示有4个行对应关系。
00 00 00 0b为一组对应,00 03 00 0c为一组对应,
00 06 00 0d为一组对应,00 0b 00 0e为一组对应。
接下来的0000 000b表示这个方法字节code第0个(也就是第一个)字节码指令与JAVA源码的11行对应。
最多两个字节表示行号,则java的方法的代码行数是有限制的。LineNumberTable的结构如下

另外一个attribute为LocalVariableTable,从0015开始,共有5个local变量。
根据如下的结构图可以分析:
这5个local变量为this,c,a,b和d。
以变量a为例字节码为 00 03 00 0b 00 22 00 08 00 02,
00 03 表示的信息为局部变量开始有效时字节码的偏移位置。
00 0b 表示的信息为作用范围覆盖的长度。上面两者结合判断作用范围。
00 22 表示的信息为名称,常量池中第34项对应的值,为const #34 = Asciz    a;
00 08 表示的信息为类型,常量池中第8项对应的值,const #8 = Asciz    I;
00 02 表示的信息为在栈中保存的位置。


方法之后是类的属性个数,attributes_count,在本例中为0001,表示有一个属性。
再接下来的0025表示attributes在常量池中的索引号,对应const #37 = Asciz    SourceFile;
表示为SourceFile属性,根据下面的SourceFile结构关系图可以得知
对应到源文件名为const #38 = Asciz    ClassFormatTest.java;

通过javap命令得到的内容如下:[javap -verbose -c ClassFormatTest > javap.txt]

Compiled from "ClassFormatTest.java"
public class ClassFormatTest extends java.lang.Object
  SourceFile: "ClassFormatTest.java"
  minor version: 0
  major version: 50
  Constant pool:
const #1 = class	#2;	//  ClassFormatTest
const #2 = Asciz	ClassFormatTest;
const #3 = class	#4;	//  java/lang/Object
const #4 = Asciz	java/lang/Object;
const #5 = Asciz	staticStr;
const #6 = Asciz	Ljava/lang/String;;
const #7 = Asciz	instanceInt;
const #8 = Asciz	I;
const #9 = Asciz	intArray;
const #10 = Asciz	[I;
const #11 = Asciz	<clinit>;
const #12 = Asciz	()V;
const #13 = Asciz	Code;
const #14 = String	#15;	//  static
const #15 = Asciz	static;
const #16 = Field	#1.#17;	//  ClassFormatTest.staticStr:Ljava/lang/String;
const #17 = NameAndType	#5:#6;//  staticStr:Ljava/lang/String;
const #18 = String	#19;	//  changed in static block
const #19 = Asciz	changed in static block;
const #20 = Asciz	LineNumberTable;
const #21 = Asciz	LocalVariableTable;
const #22 = Asciz	<init>;
const #23 = Method	#3.#24;	//  java/lang/Object."<init>":()V
const #24 = NameAndType	#22:#12;//  "<init>":()V
const #25 = Field	#1.#26;	//  ClassFormatTest.instanceInt:I
const #26 = NameAndType	#7:#8;//  instanceInt:I
const #27 = Field	#1.#28;	//  ClassFormatTest.intArray:[I
const #28 = NameAndType	#9:#10;//  intArray:[I
const #29 = Asciz	this;
const #30 = Asciz	LClassFormatTest;;
const #31 = Asciz	add;
const #32 = Asciz	(I)I;
const #33 = Asciz	c;
const #34 = Asciz	a;
const #35 = Asciz	b;
const #36 = Asciz	d;
const #37 = Asciz	SourceFile;
const #38 = Asciz	ClassFormatTest.java;

{
public static java.lang.String staticStr;


static {};
  Code:
   Stack=1, Locals=0, Args_size=0
   0:	ldc	#14; //String static
   2:	putstatic	#16; //Field staticStr:Ljava/lang/String;
   5:	ldc	#18; //String changed in static block
   7:	putstatic	#16; //Field staticStr:Ljava/lang/String;
   10:	return
  LineNumberTable: 
   line 4: 0
   line 8: 5
   line 2: 10



public ClassFormatTest();
  Code:
   Stack=5, Locals=1, Args_size=1
   0:	aload_0
   1:	invokespecial	#23; //Method java/lang/Object."<init>":()V
   4:	aload_0
   5:	iconst_1
   6:	putfield	#25; //Field instanceInt:I
   9:	aload_0
   10:	iconst_3
   11:	newarray int
   13:	dup
   14:	iconst_0
   15:	iconst_1
   16:	iastore
   17:	dup
   18:	iconst_1
   19:	iconst_2
   20:	iastore
   21:	dup
   22:	iconst_2
   23:	iconst_3
   24:	iastore
   25:	putfield	#27; //Field intArray:[I
   28:	return
  LineNumberTable: 
   line 2: 0
   line 5: 4
   line 6: 9
   line 2: 28

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      29      0    this       LClassFormatTest;


public int add(int);
  Code:
   Stack=2, Locals=5, Args_size=2
   0:	bipush	10
   2:	istore_2
   3:	bipush	23
   5:	istore_3
   6:	iload_2
   7:	iload_3
   8:	iadd
   9:	istore	4
   11:	iload	4
   13:	ireturn
  LineNumberTable: 
   line 11: 0
   line 12: 3
   line 13: 6
   line 14: 11

  LocalVariableTable: 
   Start  Length  Slot  Name   Signature
   0      14      0    this       LClassFormatTest;
   0      14      1    c       I
   3      11      2    a       I
   6      8      3    b       I
   11      3      4    d       I
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值