class文件简单解析

首先在这里贴上源代码

简单的一个第一个程序,但是不影响学习class的内容(当然是简单的好分析 ^_^)

package ttst2;

public class Tests {
	public static void main(String[] args) {
		System.out.println("helloworld");
	}
}

先把class文件也贴上
在这里插入图片描述
这里使用的sublime的hexviwer工具
之后就开始分析

class类结构

类型名称数量
u4magic1
u2minor_version1
u2major_version1
u2constant_pool_count1
cp_infoconstant_poolconstant_pool_count-1
u2access_flags1
u2this_class1
u2super_class1
u2interfaces_count1
u2interfacesinterfaces_count
u2fields_count1
field_infofieldsfields_count
u2methods_count1
method_infomethods methods_count
u2attributes_count1
attribute_infoattributesattributes_count
magic

每个class文件的开头.它的唯一作用是用来确定该文件是否为一个能被虚拟机接受的Class文件。

caff babe   
minor_version major_version

class文件的版本

0000  0034

minor_version:占2字节,次版本号,0x0000
majro_version:占2字节,主版本号,0x0034,转化为十进制为52,是使用JDK1.8编译的

constain_pool_count

常量池中常量数
好像是因为设计的时候将0保留,可能是为以后做保留
所以常量数有 constain_pool_count-1

0022

转为10进制 33个

constant_pool

根据常量池数据结构中的数值一个个对照

常量项目类型描述
CONSTANT_Utf8_infotagu1值为1
.lengthu2utf-8编码的字符串占用的字节数
.bytesu2长度为length的utf-8编码的字符串
CONSTANT_Interger_infotagu1值为3
.bytesu4按照高位在前存储的int值
CONSTANT_Float_infotagu1值为4
.bytesu4按照高位在前存储的float值
CONSTANT_Long_infotagu1值为5
.bytesu8按照高位在前存储的long值
CONSTANT_Double_infotagu1值为6
.bytesu8按照高位在前存储的double值
CONSTANT_Class_infotagu1值为7
.indexu2指向全限定名常量项的索引
CONSTANT_String_infotagu1值为8
.indexu2指向字符串字面量的索引
CONSTANT_Fieldref_infotagu1值为9
.indexu2指向声明字段的类或接口描述符 constant_class_info的索引项
.indexu2指向子段描述符constant_nameAndType的索引项
CONSTANT_Meghodref_infotagu1值为10
.indexu2指向声明方法的类描述符 classinfo的索引
.indexu2指向子段描述符constant_nameAndType的索引项
CONSTANT_InterfaceMethodref_infotagu1值为11
.indexu2指向声明方法的类描述符 classinfo的索引
.indexu2指向子段描述符constant_nameAndType的索引项
CONSTANT_NameAndType_infotagu1值为12
.indexu2指向该子段或方法名常量的索引
.indexu2指向该子段或方法描述符常量项索引
CONSTANT_MethodHandle_infotagu1值为15
.reference_kindu1值必须在1-9之内,决定了方法句柄的类型,方法句柄类型值表示方法句柄字节码行为
.regerence_indexu2值必须是对常量池的有效索引
CONSTANT_MethodType_infotagu1值为16
.descriptor_indexu2值必须是对常量池的有效索引,常量池在该索引出必须是CONSTANT_Utf8_info结构,表示方法的描述符
CONSTANT_InvokeDynameic_infotagu1值为18
.bootstrap_method_attr_indexu2值必须是对当前class文件中引导方法表的bootstrap_methods[]数组的有效索引
.name_and_type_indexu2值必须是对当前常量池的有效索引,常量池在该索引出的项必须是CONSTANT_NameAndType_info,表示方法名和方法描述符

class中常量池对应的所有16进制
在这里插入图片描述
这里贴上笔者自己分析
比如: 07 代表的是CONSTANT_Class_info,在对照表中找到CONSTANT_Class_info,根据它的长度,进行分析,如果表示标识是index,就是这个常量池对应的第几个

常量池
#1	07  class_info  
		0002 index #2
#2	01	utf8_info
		000b 11
		7474 7374 322f 5465 7374 73  
		ttst2/Tests

#3	07 class_info
		0004 index  #4
#4 	01 utf8-info
		0010     16
		6a 6176 612f 6c61 6e67 2f4f 626a 6563 74
		java/lang/Object
#5  01  utf8_info
		0006  6 
		3c69 6e69 743e <init>
#6  01
		0003   3
		28 2956   ()V
#7  01
		0004   4
		43 6f64 65  Code
#8  0A  Meghodref_info 
		0003 #3
		0009 #9
#9	0c  nameandtype_info
		0005 #5
		0006 #6
#10 01  
		000f 15
		4c69 6e65 4e75 6d62 572 5461 626c 65
		LineNumberTable
#11 01 
		0012 18
		4C6F 6361 6C56 6172 6961 626C 6554 6162 6C65
		LocalVariableTable
#12 01
		0004  4
		74 6869 73
		this
#13 01
		000d 13
		4C74 7473 7432 2F54 6573 7473 3B
		Lttst2/Tests;
#14 01 
		0004  4
		6d61 696e
		main
#15 01
		0016 22
		28 5B4C 6A61 7661 2F6C 616E 672F 5374 7269 6E67 3B29 56
		([Ljava/lang/String;)V
#16 09  Fieldref
		0011 constant_class_info  #17
		0013 constant_nameAndType  #19
#17 07  Class_info
		0012 #18   全限定名常量项的索引
#18 01
		0010 16 
		6A61 7661 2F6C 616E 672F 5379 7374 656D
		java/lang/System
#19 0c  NameAndType_info
		0014 #20
		0015 #21
#20 01
		0003  3
		6F75 74  out
#21 01
		0015 21
		4C6A 6176 612F 696F 2F50 7269 6E74 5374 7265 616D 3B
		Ljava/io/PrintStream;
#22 08  String_info
		0017  #23
#23 01
		000a 10
		68 656C 6C6F 776F 726C 64
		helloworld
#24 0a  Meghodref_info
		0019  classinfo  #25 
		001b  constant_nameAndType  #27
#25 07  Class_info
		001a 指向全限定名常量项的索引  #26
#26 01
		0013 19
		6A61 7661 2F69 6F2F 5072 696E 7453 7472 6561 6D
		java/io/PrintStream
#27 0c  NameAndType_info
		001c  #28
		001d  #29
#28 01 
		0007  7
		70 7269 6E74 6C6E
		println
#29 01
		0015 21
		28 4C6A 6176 612F 6C61 6E67 2F53 7472 696E 673B 2956
		(Ljava/lang/String;)V
#30 01
		0004  4
		61 7267 73
		args
#31 01
		0013 19
		5B4C 6A61 7661 2F6C 616E 672F 5374 7269 6E67 3B
		[Ljava/lang/String;
#32 01
		000a  10
		536F 7572 6365 4669 6C65
		SourceFile
#33 01
		000a  10
		54 6573 7473 2E6A 6176 61
		Tests.java
访问标志

分析完常量池后就是访问标志
访问标志

标志名称标志值含义
ACC_PUBLIC0x0001是否为public类型
ACC_FINAL0x0010是否被声明final,只有类可以设置
ACC_SUPER0x0020是否允许使用invokespecial字节码指令的新语意,invokespecial指令的语意在JDK 1.0.2发生过改变,为了区别这条指令使用哪种语意,JDK 1.0.2之后编译出来的类的这个标志都必须为真
ACC_INTERFACE0x0200标识这是一个接口
ACC_ABSTRACT0x0400是否为abstract类型,对于接口或者抽象类来说,此标志值为真,其他类值为假
ACC_SYNTHETIC0x1000标识这个类并非由用户代码产生
ACC_ANNOTATION0x2000标识这是一个注解
ACC_ENUM0x4000标识这是一个枚举

但是我们发现下一个u2字节是 0x0021
我们发现表中好像没有,我们只需要计算的时候加上0x0020即可(这里不是单纯的相加),就是表中的ACC_SUPER

ACC_SUPER |ACC_PUBLIC = 0x0001 | 0x0020 = 0x0021  
0x0021   //public
类索引、父类索引与接口索引集合,接口

this_class : 类索引,用于确定这个类的全限定名
super_class:父类索引,用于确定这个类父类的全限定名.Java语言不允许多重继承,故父类索引只有一个。
interfaces_count: 接口索引计数器(这里好像忘记写了继承接口了,反正是没有 *_*)
interfaces:这里没有接口,算了不管了(-_-),读者自己写一个实现接口分析一下

0021  public

0001  this_class  ttst2/Tests

0003  super_class  java/lang/Object

0000  interfaces_count
字段表集合

描述接口或者类中声明的变量,注意这里的变量只包括类变量和实例变量,而不包括方法中的局部变量.
哇,字段数为0。又不用一个一个对照了 #_#。

0000  fields_count  字段数量

在这里插入图片描述
这里贴上字段数据结构

类型名称数量描述
u2access_flags1字段访问标志
u2name_index1字段简单名称
u2descriptor_index1描述符
u2attributes_count1属性数量
u2attributesattributes_count属性值

字段中 access_flags和上方的访问标志相似
access_flags(fields)

标志名称标志值含义
ACC_PUBLIC0x0001字段是否public
ACC_PRIVATE0x0002字段是否private
ACC_PROTECTED0x0004字段是否protected
ACC_STATIC0x0008字段是否static
ACC_FINAL0x0010字段是否final
ACC_VOLATILE0x0040字段是否volatile
ACC_TRANSIENT0x0080字段是否transient
ACC_SYNTHETIC0x1000字段是否由编译器自动产生
ACC_ENUM0x4000字段是否enum

其中的attributes和方法中的基本相似。

方法集合

在这里插入图片描述

0x0002 methods_count

methods_count: 方法数量
methods:方法表集合,一组方法表类型数据的集合。方法表结构和字段表结构一样:
methods数据结构

method_info {  
    u2             access_flags;  
    u2             name_index;  
    u2             descriptor_index;  
    u2             attributes_count;  
    attribute_info attributes[attributes_count];  
}  

方法access_flags

标志名称标志值含义
ACC_PUBLIC0x0001字段是否为public
ACC_PRIVATE0x0002字段是否为private
ACC_PROTECTED0x0004字段是否为protected
ACC_STATIC0x0008字段是否为static
ACC_FINAL0x0010字段是否为final
ACC_SYNCHRONIZED0x0020字段是否为synchronized
ACC_BRIDGE0x0040方法是否是由编译器产生的桥接方法
ACC_VARARGS0x0080方法是否接受不定参数
ACC_NATIVE0x0100字段是否为native
ACC_ABSTRACT0x0400字段是否为abstract
ACC_STRICTFP0x0800字段是否为strictfp
ACC_SYNTHETIC0x1000字段是否为编译器自动产生
0001 access_flags;       public
0005   name_index; #5   <init>
0006  descriptor_index;  ()V  (void)
0001  attributes_count;   属性表计数  init方法的属性
0007  attributes_name_index;  Code  //这个代表下方的code区

在这里插入图片描述
其中的index从上发的常量表中查找对应的字符串

attribute_info (code)

这里就是code,不太懂&_&
attribute_info{
u2 attributes_name_index
u4 attribute_length
u1 max_stack
u1 max_locals
u4 code_length
u1 code[code_length]
u2 exception_table_length
exception_info exception_table[exception_table_length]
u2 attributes_count
attribute_info attributes[attribute_count]
}

code区
0007  attributes_name_index 
0000  002f  attribute_length
0001  max_stack
0001  max_locals
0000  0005 code_length 字节码指令长度
2a    code 字节码指令5个
b7
00
08
b1
0000 exception_table_length  异常数量
      exception_table   异常表
code剩余的属性
0002 attribute_count 2个属性

其中code字节码指令是jvm指令,还有没学到,读者可以查看相关资料
下方的attribute_info代表code剩下的属性 0x0002代表下方还有2个属性
在这里插入图片描述
剩下的2个属性我们可以根据接下来的
attribute_name_index 找到常量池中对应的名称
属性表集合

属性名称使用位置含义
Code方法表Java代码编译成的字节码指令
ConstantValue字段表final关键字定义的常量值
Deprecated类文件、字段表、方法表被声明为deprecated的方法和字段
Exceptions方法表方法抛出的异常
InnerClasses类文件内部类列表
LineNumberTaleCode属性Java源码的行号与字节码指令的对应关系
LocalVariableTableCode属性方法的局部变量描述
SourceFile类文件源文件名称
Synthetic类文件、方法表、字段表标识方法或字段是由编译器自动生成的

根据上表的可以找到对应的属性名称,之后根据对应的结构去分析
发现是LineNumberTable

000a  attribute_name_index 000a #10  
		LineNumberTable

LineNumberTable属性表结构

类型名称数量
u2attribute_name_index1
u4attribute_length1
u2line_number_table_length1
line_number_infoline_number_tableline_number_table_length

lineNumberTable中的line_number_info结构

类型名称数量说明
u2start_pc1字节码行号
u2line_number1java源码行号
000a  attribute_name_index 000a #10  
	    	LineNumberTable
0000 0006  attribute_length 属性长度 
0001 line_number_table_length 
	line-number_table:
	0000    start_pc  字节码行号
	0004    line_number	 java源码行号

在这里插入图片描述
之后根据这个方法查找下一个属性

000b  attribute_name_index 
		LocalVariableTable

根据上方属性表集合找到此属性
LocalVariableTable属性表结构

类型名称数量
u2attribute_name_index1
u4attribute_length1
u2local_variable_table_length1
local_variable_infolocal_variable_tablelocal_variable_table_length

local_variable_info

类型名称数量说明
u2start_pc1局部变量的生命周期开始的字节码偏移量
u2length1局部变量作用范围覆盖的长度
u2name_index1局部变量的名称
u2descriptor_index1这个局部变量的描述符
u2index1这个局部变量在栈帧局部变量表中Slot的位置。当这个变量数据类型是64位类型时(double和long),它占用的Slot为index和index+1两个
000b  attribute_name_index 
		LocalVariableTable
0000 000c   attribute_length 属性长度 
0001  local_variable_table_length
	local_variable_table_length:
	0000 start_pc
	0005 length;	代表了这个局部变量的生命周期开始的字节码偏移量及其作用范围覆盖的长度
	000c name_index;  
		this  分别代表了局部变量的名称
	000d descriptor_index;  Lttst2/Tests;  局部变量的描述符
	0000 index   这个局部变量在栈帧局部变量表中Slot的位置

在这里插入图片描述
到这里第一个方法就分析完成了。。

第二个方法
这个方法就是main方法,开始的access_flags就是8+1,分别对应public和static

0009  access_flags   8+1  static public
000E  name_index  
			main
000f  descriptor_index  
			([Ljava/lang/String;)V
0001  attributes_count 

在这里插入图片描述
其中0x0001代表method的方法有1个
继续分析发现接下来又是一个code区

0007  attributes_name_index  Code
0000 0037  attributes_length  
0002  max_stack
0001  max_locals
0000 0009 code_length 
b2     code 字节码指令9个
00
10
12
16
b6
00
18
b1
0000  exception_table_length 
		exception_table
0002  attributes_count  2个属性

在这里插入图片描述
这里又是两个属性,我敢大胆猜测,又是上面的两个code属性.(|^_😃)

第一个属性 
000a attribute_name_index   #10
		LineNumberTable
0000 000a attribute_length 
0002 line_number_table_length 
	line_number_table0 
	0000  start_pc  字节码行号
	0006 line_number java源码行号
	line_number_table1
	0008  start_pc  字节码行号
	0007  line_number java源码行号
第二个属性  
000B  attribute_name_index #8
		LocalVariableTable
0000 000c attribute_length
0001  local_variable_table_length  
	local_variable_table
	0000  start_pc
	0009  length
	001e  name_index 
			args
	001f descriptor_index
			[Ljava/lang/String;
	0000  line_number java源码行号

在这里插入图片描述
方法都分析完了,发现还有最后的attributes_count,attributes_info(打起精神,马上就完了*—*)
我推测就是 属性表中的SourceFile(我现在都不知道是不是%_%)
属性表集合

属性名称使用位置含义
Code方法表Java代码编译成的字节码指令
ConstantValue字段表final关键字定义的常量值
Deprecated类文件、字段表、方法表被声明为deprecated的方法和字段
Exceptions方法表方法抛出的异常
InnerClasses类文件内部类列表
LineNumberTaleCode属性Java源码的行号与字节码指令的对应关系
LocalVariableTableCode属性方法的局部变量描述
SourceFile类文件源文件名称
Synthetic类文件、方法表、字段表标识方法或字段是由编译器自动生成的
classFile最后一个属性
0001 attribute_count
0020 attribute_name_index 
		SourceFile
0000 0002  attribute_length  2
0021 
		Tests.java

在这里插入图片描述
最后根据我们的分析和javap生成的对照,发现没有错误(^_^)
在这里插入图片描述
在这里插入图片描述

ps

1,全限定名:将类全名中的“.”替换为“/”,为了保证多个连续的全限定名之间不产生混淆,在最后加上“;”表示全限定名结束。例如:“com.test.Test"类的全限定名为"com/test/Test;”
2,简单名称:没有类型和参数修饰的方法或字段名称。例如:"public void add(int a,int b){…}“该方法的简单名称为"add”,"int a = 123;“该字段的简单名称为"a”
3,描述符:描述字段的数据类型、方法的参数列表(包括数量、类型和顺序)和返回值。根据描述符规则,基本数据类型和代表无返回值的void类型都用一个大写字符表示,而对象类型则用字符L加对象全限定名表示
4,Slot,虚拟机为局部变量分配内存所使用的最小单位,长度不超过32位的数据类型占用1个Slot,64位的数据类型(long和double)占用2个Slot
5. 无符号数:是基本数据类型,以u1、u2、u4、u8分别代表1个字节、2个字节、4个字节、8个字节的无符号数,可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成的字符串值。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值