Java 源文件Class详解

本文详细分析了Java Class文件的结构,包括魔数、版本号、常量池、类和接口信息、字段、方法、属性等关键部分,并通过实例展示了如何解析字节码和常量池中的内容。

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

目录

一、概述

二、参考


一、概述

只是对class做一次完整的分析,不涉及理论介绍。

1.源文件

package com.gyw.practice;

public class TestClass{
	private int m;
	public int inc(){
		return m + 1;
	}
}

2.字节流文件

 3.class文件分析

u4 magic;魔数,4个字节:0xCAFEBABE

u2 minor_version;次版本号,2个字节:0x0000(0)

u2 major_version;主版本号,2个字节:0x0034(52)

u2 constant_pool_count;常量池大小,2个字节:0x0013(19) (索引从1开始,左闭右开,18个常量)

cp_info constant_pool[constant_pool_count-1]; cp_info 表,常量池

// 常量池每一项的数据结构
cp_info {  
    u1 tag; // 标识位
    u1 info[]; // 表
}

#1:tag,1个字节,0x0A(10),CONSTANT_Methodref,类方法符号引用;class_index,2个字节,0x0004(4),指向#4;name_and_type_index,2个字节,0x000F(15),指向#15;

info {  
     u1 tag; // 标识位
     u2 class_index; // 类描述索引
     u2 name_and_type_index; // 名称和类型描述索引
}

 #2:tag,1个字节,0x09(9),CONSTANT_Fieldref,字段符号引用;class_index,2个字节,0x0003(3),指向#3;name_and_type_index,2个字节,0x0010(16),指向#16;

info {  
     u1 tag; // 标识位
     u2 class_index; // 类描述索引
     u2 name_and_type_index; // 名称和类型描述索引
}

#3:tag,1个字节,0x07(7),CONSTANT_Class,类或接口的符号引用;name_index,2个字节,0x0011(17),指向#17;

CONSTANT_Class_info {  
    u1 tag;
    u2 name_index;
}

#4:tag,1个字节,0x07(7),CONSTANT_Class,类或接口的符号引用;name_index,2个字节,0x0012(18),指向#18;

#5:tag,1个字节,0x01(1),CONSTANT_Utf8,utf8字符串常量;length,2个字节,0x0001(1),数组大小为1;bytes数组,1个字节,0x6D(109), ASCII字符m;

总结为:m

CONSTANT_Utf8_info {  
    u1 tag; // 标志位
    u2 length; // bytes数组的长度(多少个8位字节)
    u1 bytes[length]; // 字符串经编码之后的byte数组,编码格式为改进过的UTF-8
}

#6:tag,1个字节, 0x01(1),CONSTANT_Utf8,utf8字符串常量;length,2个字节,0x0001(1),数组大小为1;bytes数组,1个字节,0x49(73), ASCII字符I(大写的 i );

总结为:I

 #7:tag,1个字节,0x01(1),CONSTANT_Utf8,utf8字符串常量;length,2个字节,0x0006(6),数组大小为6;bytes数组,6个字节,0x3C(60), ASCII字符< || 0x69(105), ASCII字符i ||0x6E(110), ASCII字符n || 0x69(105), ASCII字符i  || 0x74(116), ASCII字符t || 0x3E(62), ASCII字符>;

总结为:<init>

#8:tag,1个字节, 0x01(1),CONSTANT_Utf8,utf8字符串常量;length,2个字节,0x0003(3),数组大小为3;bytes数组,3个字节,0x28(40), ASCII字符(|| 0x29(41), ASCII字符)|| 0x56(86), ASCII字符V;

总结为:()V

#9:tag,1个字节, 0x01(1),CONSTANT_Utf8,utf8字符串常量;length,2个字节,0x0004(4),数组大小为4;bytes数组,4个字节,0x43(67), ASCII字符C || 0x6F(111), ASCII字符o || 0x64(100), ASCII字符d || 0x65(101), ASCII字符e;

总结为:Code

#10:tag,1个字节, 0x01(1),CONSTANT_Utf8,utf8字符串常量;length,2个字节,0x000F(15),数组大小为15;bytes数组,15个字节,0x4C(76),ASCII字符L ||0x69(105),ASCII字符i ||0x6E(110),ASCII字符n ||0x65(101),ASCII字符e ||0x4E(78),ASCII字符N ||0x75(117),ASCII字符u ||0x6D(109),ASCII字符m ||0x62(98),ASCII字符b ||0x65(101),ASCII字符e ||0x72(114),ASCII字符 r||0x54(84),ASCII字符T ||0x61(97),ASCII字符a ||0x62(98),ASCII字符b ||0x6C(108),ASCII字符l ||0x65(101),ASCII字符e ;

总结为:LineNumberTable

#11 : tag, 1个字节,0x01(1),CONSTANT_Utf8,utf8字符串常量;length,2个字节,0x0003(3),数组大小为3;bytes数组,3个字节,0x69(105),ASCII字符i || 0x6E(110),ASCII字符n ||  0x63(99),ASCII字符c ;

总结为:inc

#12:tag, 1个字节,0x01(1),CONSTANT_Utf8,utf8字符串常量;length,2个字节,0x0003(3),数组大小为3;bytes数组,3个字节,0x28,0x29,0x49;

总结为:()I

#13:tag,1个字节,0x01(1),CONSTANT_Utf8,utf8字符串常量;length,2个字节,0x000A(10),数组大小为10;bytes数组,10个字节,0x53,0x6F,0x75,0x72,0x63,0x65,0x46,0x69,0x6C,0x65;

总结为:SourceFile

#14:tag,1个字节,0x01(1),CONSTANT_Utf8,utf8字符串常量;length,2个字节,0x000E(14),数组大小为10;bytes数组,10个字节,0x54,0x65,0x73,0x74,0x43,0x6C,0x61,0x73,0x73,0x2E,0x6A,0x61,0x76,0x61;

总结为:TestClass.java

#15:tag,1个字节,0x0C(12),CONSTANT_NameAndType,字段或方法的描述符;name_index,2个字节,名称索引,

0x0007(7),指向#7;descriptor_index,2个字节,0x0008(8),CONSTANT_Utf8类型常量项的索引,里面存储了字段、方法的描述符,指向#8;

总结为:<init>:()V

CONSTANT_NameAndType_info {  
    u1 tag;
    u2 name_index;
    u2 descriptor_index;
}

#16:tag,1个字节, 0x0C(12),CONSTANT_NameAndType,字段或方法的描述符;name_index,2个字节,名称索引,

0x0005(5),指向#5;descriptor_index,2个字节,0x0006(6),CONSTANT_Utf8类型常量项的索引,里面存储了字段、方法的描述符,指向#6;

总结为:m:I

#17:tag,1个字节,0x01(1),CONSTANT_Utf8,utf8字符串常量;length,2个字节,0x001A(26),数组大小为26;bytes数组,26个字节,

0x63,0x6F,0x6D,0x2F,0x67,0x79,0x77,0x2F,0x70,0x70,0x61,0x63,0x74,0x69,0x63,0x65,0x2F,0x54,0x65,0x73,0x74,0x43,0x6C,0x61,0x73,0x73;

总结为:com/gyw/practice/TestClass

#18:tag,1个字节,0x01(1),CONSTANT_Utf8,utf8字符串常量;length,2个字节,0x0010(16),数组大小为16;bytes数组,16个字节,

0x6A,0x61,0x76,0x61,0x2F,0x2C,0x61,0x6E,0x67,0x2F,0x4F,0x62,0x6A,0x65,0x63,0x74;

总结为:java/lang/Object

u2 access_flags;访问标志

占用2个字节,0x0021 ,代表ACC_PUBLIC(0x0001)和 ACC_SUPER(0x0020),即public class;
u2 this_class;当前类名的索引,引用常量池中的utf8常量;

占用2个字节,0x0003(3),指向常量池中#3,类的符号引用,类名指向#17:com/gyw/practice/TestClass;
u2 super_class;父类名的索引,引用常量池中的utf8常量;

占用2个字节,0x0004(4),指向常量池中#4,类的符号引用,类名指向#18:java/lang/Object
u2 interfaces_count;该类直接实现的接口数或是该接口的直接父接口数。

占用2个字节,0x0000,0个接口实现;
u2 interfaces[interfaces_count]; 2个字节数组,列表每一项保存常量池中CONSTANT_Class类型常量的索引。

ps:测试类中没写接口。。
u2 fields_count;该类或者接口所拥有的字段数

占用2个字节,0x0001(1),1个字段
field_info fields[fields_count];列表每一项为field_info表结构

分析1个字段(本类中只用1个字段):

access_flags,访问标识,2个字节,0x0002(ACC_PRIVATE),即:private;

name_index,2个字节,0x0005(5),指向CONSTANT_Utf8类型常量项的索引,里面存储了字段名称,#5:m;

descriptor_index,CONSTANT_Utf8类型常量项的索引,里面存储了字段描述符,2个字节,0x0006(6),指向常量池#6:I;

attributes_count,当前字段拥有的attribute数量,2个字节,0x0000,0个属性;

attributes:属性数组,每一项为attribute_info结构数据;

field_info {  
    u2 access_flags; // 访问标志
    u2 name_index; // 字段名的索引
    u2 descriptor_index; // 描述器索引
    u2 attributes_count; // 属性个数
    attribute_info attributes[attributes_count]; // 属性表数组
}


u2 methods_count;该类或接口拥有的方法数

2字节,0x0002(2),2个;
method_info methods[methods_count];数组,每一项为method_info表结构

method_info {  
    u2 access_flags; // 访问标识符
    u2 name_index; // 方法名索引
    u2 descriptor_index; // 方法描述符索引
    u2 attributes_count; // 方法属性个数
    attribute_info attributes[attributes_count]; // 方法属性数组
}

access_flags:用于表示方法的访问属性,2个字节,0x0001,ACC_PUBLIC,即public;

name_index:CONSTANT_Utf8类型常量项的索引,里面存储了方法名称,2个字节,0x0007(7),指向常量池中#7:<init>;

descriptor_index:CONSTANT_Utf8类型常量项的索引,里面存储了方法描述符,2个字节,0x0008(8),指向常量池中#8:()V;

attributes_count:当前方法拥有的attribute数量;2个字节,0x0001,1个属性;

attributes:属性数组,每一项为attribute_info结构数据;

attributes_name_index:为CONSTANT_Utf8类型常量项的索引,表示attribute的名称,2个字节,0x0009,指向常量池索引#9:Code属性;

attribute_info {  
    u2 attribute_name_index;
    u4 attribute_length;
    u1 info[attribute_length];
}

Code属性:Code属性用于保存方法、类初始化方法、对象初始化方法内指令等相关信息。

Code属性的结构如下:

Code_attribute {  
    u2 attribute_name_index; // 属性名称索引,2个字节,0x0000
    u4 attribute_length; // 属性长度,值为整个Code属性减去attribute_name_indexattribute_length的长度,4个字节,0x0000001D(29);
    u2 max_stack; // 当前方法执行时的最大栈深度,0x0001,1
    u2 max_locals; // 当前方法分配的局部变量个数,包括调用方式时传递的参数。long和double类型计数为2,其他为1;0x0001,1
    u4 code_length; // 方法编译后的字节码的长度,0x00000005,5
    u1 code[code_length]; // 方法编译后的字节码,0x2A(42);0xB7;0x00;0x01;0xB1;
    u2 exception_table_length; // exception_table的长度,0x0000,0;
    {
        u2 start_pc;
        u2 end_pc;
           u2 handler_pc;
           u2 catch_type;
    } exception_table[exception_table_length];
    u2 attributes_count; // 该exception_table拥有的attribute数,0x0001(1)
    attribute_info attributes[attributes_count]; // 0x000A(10)
}

 [attribute_name_index,0x000A(10),#10,LineNumberTable;]

LineNumberTable属性

LineNumberTable属性用于确定方法执行时,指令码对应的源文件行号。

LineNumberTable属性的结构如下:

LineNumberTable_attribute {  
    u2 attribute_name_index; // 0x0000
    u4 attribute_length; // 0x00060001 (393217)
    u2 line_number_table_length; // 0x0000
    {   u2 start_pc;
        u2 line_number;    
    } line_number_table[line_number_table_length];
}

start_pc为code[]数组元素的索引。

line_number为对应源文件代码的行号。

u2 attributes_count;
attribute_info attributes[attributes_count];

(PS:到最后的属性处,分析的有点乱了)

 

二、参考

详细的介绍可以参考:

http://tech.dianwoda.com/2018/03/28/jvm-classjie-xi-wen-jian-ge-shi/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值