欢迎转载,转载需声明出处

------------------

请先看上一篇:Class类文件结构浅析

上一篇讲的都是理论。以下我们亲自实践一下。

首先编写一个简单的java类:
public class Test
{
private int m;
private String str; public int func(int m,String str)
{
str += "OK";
m = 10;
return -1;
} public static void main(String[] arg)
{
String str = "test";
int m = 20;
new Test().func(m,str);
}
}
紧接着我们使用javac编译器将其编译为class字节码格式。这篇文章以分析这个类的字节码为主。分析之前先介绍两个工具,一个是winHex,16进制编辑器,能够以16进制的形式查看class源文件。还有一个是jdk自带的反编译工具javap。我们能够一边分析16进制数据,一边对比着javap编译后的结果。

首先使用javap -verbose命令查看反编译结果。

C:\Users\Rowand jj\Desktop>javap -verbose Test
Classfile /C:/Users/Rowand jj/Desktop/Test.class
Last modified 2014-5-2; size 616 bytes
MD5 checksum cfd6b2e7d9d99dda3b84d1dbe87ce657
Compiled from "Test.java"
public class Test
SourceFile: "Test.java"
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #11.#26 // java/lang/Object."<init>":()V
#2 = Class #27 // java/lang/StringBuilder
#3 = Methodref #2.#26 // java/lang/StringBuilder."<init>":(
)V
#4 = Methodref #2.#28 // java/lang/StringBuilder.append:(Lj
ava/lang/String;)Ljava/lang/StringBuilder;
#5 = String #29 // OK
#6 = Methodref #2.#30 // java/lang/StringBuilder.toString:(
)Ljava/lang/String;
#7 = String #31 // test
#8 = Class #32 // Test
#9 = Methodref #8.#26 // Test."<init>":()V
#10 = Methodref #8.#33 // Test.func:(ILjava/lang/String;)I
#11 = Class #34 // java/lang/Object
#12 = Utf8 m
#13 = Utf8 I
#14 = Utf8 str
#15 = Utf8 Ljava/lang/String;
#16 = Utf8 <init>
#17 = Utf8 ()V
#18 = Utf8 Code
#19 = Utf8 LineNumberTable
#20 = Utf8 func
#21 = Utf8 (ILjava/lang/String;)I
#22 = Utf8 main
#23 = Utf8 ([Ljava/lang/String;)V
#24 = Utf8 SourceFile
#25 = Utf8 Test.java
#26 = NameAndType #16:#17 // "<init>":()V
#27 = Utf8 java/lang/StringBuilder
#28 = NameAndType #35:#36 // append:(Ljava/lang/String;)Ljava/l
ang/StringBuilder;
#29 = Utf8 OK
#30 = NameAndType #37:#38 // toString:()Ljava/lang/String;
#31 = Utf8 test
#32 = Utf8 Test
#33 = NameAndType #20:#21 // func:(ILjava/lang/String;)I
#34 = Utf8 java/lang/Object
#35 = Utf8 append
#36 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#37 = Utf8 toString
#38 = Utf8 ()Ljava/lang/String;
{
public Test();
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 1: 0 public int func(int, java.lang.String);
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=3
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder.
"<init>":()V
7: aload_2
8: invokevirtual #4 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
11: ldc #5 // String OK
13: invokevirtual #4 // Method java/lang/StringBuilder.
append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
16: invokevirtual #6 // Method java/lang/StringBuilder.
toString:()Ljava/lang/String;
19: astore_2
20: bipush 10
22: istore_1
23: iconst_m1
24: ireturn
LineNumberTable:
line 8: 0
line 9: 20
line 10: 23 public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=3, args_size=1
0: ldc #7 // String test
2: astore_1
3: bipush 20
5: istore_2
6: new #8 // class Test
9: dup
10: invokespecial #9 // Method "<init>":()V
13: iload_2
14: aload_1
15: invokevirtual #10 // Method func:(ILjava/lang/String
;)I
18: pop
19: return
LineNumberTable:
line 15: 0
line 16: 3
line 17: 6
line 18: 19
}

大家可能看的有点晕,不要紧,我们一步一步来分析。以下我们使用winhex打开Test.class文件:


准备工作都做好了,以下開始分析吧。假设你对class文件一无所知,请先阅读上一篇文章。
通过上一篇文章,我们了解了class文件的基本结构,首先来回想一下,class文件是由一组以8位字节为基础单位的二进制流,各个数据项目严格依照顺序紧凑的排列在一起,中间没有不论什么分隔符。

class文件格式以一种类似c语言结构体的伪结构存储,这样的伪结构仅仅有两种数据类型:

无符号数和表。无符号数是基本数据类型,以u1 u2 u4 u8代表1字节 2字节 4字节 8字节,表是由多个无符号数或者其它表构成的复合结构。

整个class文件就是一张表,其结构例如以下:

ClassFile {
u4 magic;//魔数(0xCAFEBABE)
u2 minor_version;//次版本
u2 major_version;//主版本
u2 constant_pool_count;//常量池容量计数值
cp_info constant_pool[constant_pool_count-1];//常量池
u2 access_flags;//訪问标志
u2 this_class;//类索引
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];//属性表集合
}
以下看头四个字节,正好是0xCAFEBABE,即所谓的魔数。紧接着四个字节是0x0000 0033。相应10进制为51,这代表当前class文件的版本,查表后发现是JDK1.7.0。

接下来就是所谓的常量池了,首先是常量池计数器,值为0x0027,相应10进制为39,说明一共同拥有38项常量(第0项保留),对比javap反编译后的汇编代码也能够证实(#38)。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hkamo=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

常量池中的项目类型以一个u1类型的tag标识,比方0x0A(offset为0000 000A),十进制为10,这个tag代表的是CONSTANT_Methodref_info类型(查表可知),表示对类中方法的符号引用。

紧接着两个字节(u2)为类描写叙述符索引,值为0x000B(第11项常量池数据项),再接着的两个字节是名称和类型描写叙述符,值为0x001A(第26项常量池数据项)。这四个字节都引用了常量池其它数据项。反编译的结果例如以下,能够看到方法的类描写叙述符为java/lang/Object,名称和类型描写叙述符"<init>":<v>.



watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hkamo=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">


第一项常量分析完成,咱们往下看,紧接着一个字节是0x07,该tag相应的表是CONSTANT_Class_info类型:



所以紧随其后的两个字节为指向全限定名的常量项的索引,值为0x001B,指向常量池第27项常量。例如以下:




能够发现第27项常量的头一个字节为0x01,即CONSTANT_Utf8_info:



所以接下来的两个字节为字符串所占的字节数。0x0017。即十进制的23,所以接下来的23个字节即为字符串值。查看winhex可知,值为:java/lang/StringBuilder:



对比一下汇编代码:


说明我们解读没有不论什么问题。

第二项常量分析完成,看第三项,tag值为0x0A,又是一个

CONSTANT_Methodref_info,依照上面的分析流程分析....
因为常量池太过庞大,这里仅分析到这。后面的大家能够自己分析,然后对比汇编代码验证你的分析结果。

常量池之后的表项为access_flags,即訪问标志,占两个字节(u2)。怎样在winhex中找到两个字节呢?这里有个技巧,通过刚才的汇编代码发现常量池最后一项常量为#38,该常量后即为訪问标志位:


在winhex中找到这个串:

所以红色圈出来的两个字节即为訪问标志,0x0021。

这正好是0x0020|0x0001(查表),所以訪问标志为ACC_PUBLIC,ACC_SUPER。查看汇编代码:



验证了我们的解析结果,是不是非常easy?
接下来各自是this_class和super_class即本类索引和父类索引,
this_class值为0x0008,类索引为8。即常量池的第8项:



super_class值为0x000B,类索引为11,即常量池的第11项:



能够清晰的看出类名为Test。父类为Object。
接下来是接口计数器。值为0x0000,代表这个类没有实现不论什么接口。

至此。我们分析完了类索引。父类索引,接口索引。

以下到了字段表集合了。

首先两个字节为字段表计数器。值为0x0002:




有两个字段。咱仅仅分析第一个字段。

头两个字节0x0002为訪问标志,相应的是private:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hkamo=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

紧接着两个字节为字段简单名称。0x000C,指向常量池的第12项,值为m。然后0x000D为字段描写叙述符,指向常量池的第13项,值为I。代表Int,最后两个字节为属性表,值为0x0000。到这里我们能够猜測源文件里有个私有成员变量定义:private int m,与事实相符!接下来第二个字段为private String str。

字段表分析到这,接下来是方法表。事实上方法表跟字段表格式是基本一致的,依照上面流程就可以。首先是方法表计数器0x0003,代表有三个方法(多的那个方法是<init>方法),看第一个,訪问标志位0x0001,相应的是public,然后名称索引位0x0010,指向第16项常量,这是名称索引:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY2hkamo=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

然后是0x0011,为描写叙述符索引:

即为init方法。
好了,方法表就分析到这了。

整个class文件也分析的差点儿相同了。相信大家看完之后对class文件结构会有进一步的了解的。

class文件结构浅析(2)的更多相关文章

  1. 《FLASH CC 2015 CANVAS 中文教程》——1、导出canvas动画,文件结构浅析

    注::如果你对 FLASH 这个软件操作不够熟悉,建议你可以先看看FLASH动画之类的书. :FLASH CC 在文中直接简称为CC. :以下所以文章中所说的快捷键 如果你按了不起作用,请检查是否有其 ...

  2. .net core 项目文件结构浅析

    1:launch.json (配置调试用的) 通过vs code创建的项目,都会有这个文件,是启动调试的配置文件: (vscode默认支持nodejs调试) 要调试调试c#代码  需要安装 C# 插件 ...

  3. MXF文件结构浅析

    MXF是英文Material eXchange Format(素材交换格式)的缩语.MXF是SMPTE(美国电影与电视工程师学会)组织定义的一种专业音视频媒体文件格式.MXF主要应用于影视行业媒体制作 ...

  4. 浅析BMP位图文件结构(含Demo)

    浅析BMP位图文件结构(含Demo) 作者:一点一滴的Beer http://beer.cnblogs.com/   关于BMP位图格式在网上可以找到比较详细的相关文档,有兴趣的可以搜索标题为“BMP ...

  5. Mybatis配置信息浅析 MyBatis简介(二)

    官方文档入门篇中有明确说明 每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的. SqlSessionFactory 的实例可以通过 SqlSessionF ...

  6. Java网络编程和NIO详解8:浅析mmap和Direct Buffer

    Java网络编程与NIO详解8:浅析mmap和Direct Buffer 本系列文章首发于我的个人博客:https://h2pl.github.io/ 欢迎阅览我的CSDN专栏:Java网络编程和NI ...

  7. 宋牧春: Linux设备树文件结构与解析深度分析(1) 【转】

    转自:https://mp.weixin.qq.com/s/OX-aXd5MYlE_YoZ3p32qWA 作者简介 宋牧春,linux内核爱好者,喜欢阅读各种开源代码(uboot.linux.ucos ...

  8. 焦大:逛网seo案例浅析

    http://www.wocaoseo.com/thread-93-1-1.html 逛,发现喜欢.这或许是很多人上网的喜欢方式,我隐约记得白鸦在一次采访上说到现在人的购物方式,在淘宝上人们决定买一件 ...

  9. 浅析Linux用户空间中的Mmap

    一.MMap基础概念 mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系.实现这样的映射关系后,进程就可以采 ...

随机推荐

  1. python 学习分享-字典篇

    python字典(Dictionary) dict是无序的 key必须是唯一切不可变的 a={'key1':'value1','key2':'value2'} 字典的增删改查 a['key3']='v ...

  2. Leetcode 472.连接词

    连接词 给定一个不含重复单词的列表,编写一个程序,返回给定单词列表中所有的连接词. 连接词的定义为:一个字符串完全是由至少两个给定数组中的单词组成的. 示例: 输入: ["cat" ...

  3. .Net程序员自学dump分析教程

    文章:.Net程序员自学dump分析教程 可以分析内存状态.

  4. hdu5441

    Travel Time Limit: 1500/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Su ...

  5. 归并排序,时间复杂度nlogn

    思路: /*   考点:   1. 快慢指针:2. 归并排序.   此题经典,需要消化吸收.   复杂度分析:              T(n)            拆分 n/2, 归并 n/2 ...

  6. http请求代理proxy-ajax

    今天把项目中的反向代理脚本程序抽成了一个插件,通过配置文件配置代理的http请求,这样使用起来比较方便,每位开发成员都可以用自己配置的代理调试代码.也可以用来直接做http代理,你甚至都不用Charl ...

  7. wamp出现You don’t have permission to access/on this server提示

    本地搭建wamp,输入http://127.0.0.1访问正常,当输入http://localhost/,apache出现You don't have permission to access/on ...

  8. 记录vim经常使用的几个命令

    vi/vim 基本使用方法 vi编辑器是所有Unix及Linux系统下标准的编辑器. $ vim 1.txt 以vi打开一个文件就直接进入一般模式了(这是默认的模式).在这个模式中, 你可以使用上下左 ...

  9. Django REST framework 的TokenAuth认证及外键Serializer基本实现

    一,Models.py中,ForeignKey记得要有related_name属性,已实现关联对象反向引用. app_name = models.ForeignKey("cmdb.App&q ...

  10. 如何快速定位TempDB产生问题

    步骤1.TempDB压力诊断 等待类型诊断 TempDB的争用压力在等待篇中已经简单介绍,等待的表现为 pagelatch_类等待,等待的资源是 “2: X :X ” tempDB所在磁盘的响应时间 ...