看懂class文件 转
前言
现在周六公司进行一系列的java培训,刚上来就给我看class文件,比较头疼,不过感觉还是学到了一些东西,毕竟像老大说的,想要变得牛逼,是需要多学习多看的。好了,闲话不多说,我整理了一下思路,记录一下自己的学习过程,以后如果有时间的话,我会每个周日整理自己的笔记。我是菜鸡,大家不喜勿喷啊。
曾经风靡前世界的Wirte once,Run Anywhere,让Java这门语言在编程语言上大放异彩,至今仍然保持着第一受欢迎的地位。虽然Sun公司已经被收购,詹姆斯~高斯林前段时间找工作还被歧视年纪大,但我们还是对这门语言充满信心。好了,吹多了。Write once,Run AnyWhere基础实现就是虚拟机(JVM)和字节码储存格式。当然了,这里我主要还是记录一下字节码格式了,JVM以后有时间再学习吧。
这里的字节码格式,就是我们今天要说的Class文件了。当然了,这种说法如果考究起来还是不那么贴切的。因为任何一个Class文件都对应着唯一一个类和接口的定义信息,但是反过来说吧,类和接口并不一定非得在文件中(因为有些类或者接口可以通过classLoader自动生成)。
魔数 版本号
Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件中,中间没有添加任何分隔符,就像下面的这样:
vc+28c+wtcSjrLWryse21MDtveLJz8PmxMfQqcr919a7ucrH09Cw79b6tcShozwvcD4NCjxwPs7et/u6xcr9yvTT2rv5sb61xMr9vt3A4NDNo6zV4sDvy/y2qNLlwct1MaGidTKhonU0oaJ1OMC0tPqx7TG49tfWvdqhojK49tfWvdqhojS49tfWvdq6zTi49tfWvdq1xM7et/u6xcr9oaPL/L/J0tTD6Mr2uty24LarzvejrLHIyOfK/dfWoaLL99L90v3Tw6Giyv3Bv9a1u/LV31VURi04seDC67m5s8m1xNfWt/u0rta1oaM8L3A+DQo8cD66w8HLo6zP1tTav6rKvLfWzvbO0sPHtcRjbGFzc87EvP7By6Os1abDx9K70NDSu9DQwLSjrMbk1tC63LbgwcujrLa8ysdKYXZh0OnE4rv6tcS55re2o6zL+dLUztLDx8HLveLSu8/Cvs3Q0MHLo6zPyL+0tdrSu9DQo7o8L3A+DQo8cD48aW1nIGFsdD0="这里写图片描述" src="/uploadfile/Collfiles/20170612/20170612092423657.png" title="\" />
第一行第一个红框,4个字节,如果看成英语,那么就是cafebabe了,也就是我们的咖啡了,你看java的logo是不是就是个咖啡样子啊?这四个字节在被称之为魔数(Magic Number),唯一的作用不是为了好玩,是为了判断是否成为虚拟机接受的class文件。当然像这种判断方式有很多了,我们经常用的图片的格式并不是以后缀名png,jpg来判断的,而是通过图片的头文件的数据来判断的,哎,这里吐槽一下,刚开始第一家公司写项目的时候,后台就是根据后缀名去剪切图像,经常出现图片不能保存的错误,搞得我么 一度很尴尬啊。
第二个红框,也就是第5个和第6个字节代表的是次版本号(是JDK的版本号,不是你写程序的版本号),看上去都是0啊。
第三个红框,也是00 34 第7和第8个字节,16进制的转成十进制的就是52了,这地方得说一下,它代表的是主板本号(Major Version)。Java的版本号是从45开始的,JDK1.1之后每一个大版本发布主版本号向上+1,高版本的JDK能向下兼容以前的老版本的class文件,但是不能运行以后的class文件,即使文件格式未发生任何变化,虚拟机也必须拒绝执行超过其版本号的class文件。
举个例子啊,我的目前运行的是jdk1.8,版本就是52了,虽然我可以执行jdk1.7生成的class文件,但是jdk1.7的环境运行不了我1.8生成的class文件。下图是我在书上找的,可能比较老,还没有到1.8的内容,打个tag吧,JDK 1.8.0_40的major version是52。

现在还介绍最后一个框00 21的意思了。这个是常量池的入口了。常量池可以理解为Class文件之中的资源仓库,它是Class文件结构中与其它项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一。
常量池
每一个Class文件的常量池都不是固定的,所以有一个u2类型(也就是2个字节的)数据,来记录常量池的个数,但是这个个数与Java语言习惯不一样,它是从1开始计数的,也就是说00 21本来是有33个常量池,但是事实只有32个。关于这个问题的设计,可以追究到Class文件格式规范制定之时,设计者将第0项常量空出来是有特殊考虑的,这样做的目的是在于满足后面的某些指向常量池的索引值的数据在特定情况下表达“不引用任何一个常量池项目”的含义,这样情况及可以把索引值置为0 来表示。
当然了,常量池并不是我们想的那样,值放我们的public static final int _COUNT = 1这种常量值了,它主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References).这里的字面量就像我们Java语言层面的常量概念,就是文本常量,final型的常量值等等。而符号引用是属于编译原理方面的概念,包含了下面的常量:
1.类和接口的全限定名(Fully Qualified Name)
2.字段的名称和描述符(Descriptor)
3.方法的名称和描述符
当然了,这些都是什么意思,可能需要等我学习深入之后才能懂的吧。
到这里了,是不是应该来看这些常量池都是些什么东西吧。不急,想了解这个常量池的内容,还需要知道这张表的含义,Class文件就是通过查询这两张表,还获取常量池的内容。首先需要知道常量池中14项常量项的结构表:


大家看到这边表可能不知道什么意思,那好,我们就用程序来说明一下,写个hello world程序:
|
1
2
3
4
5
6
7
8
9
10
|
public class T { //字符常量 public static final int COUNT_NUMBER = 1 ; //main函数 public static void main(String[] args) { System.out.println("hello world"); }} |
编译的T文件用UltraEdit打开,如下图:

紧靠在00 21后面的 就是我们的第一个常量池了,请看第一个红框,为什么是一个字节呢,因为常量池中最多14中不同的数据结构,一个字节(255)足够了,节约资源嘛。那么第一个红框中是0A,换成十进制就是10了,好了这次要查表了,什么表,就是上图那个表了啊,

对,我们看到了是MethodRef_info,它的这种数据结构有三个属性组成:tag,index,index。tag值为10,标记是MethodRef属性的,还有两个index,index分别表示方法描述符的索引项和类型描述符的索引项,这里就不深究了,因为今天的内容只是为了读懂这个class文件,但是需要注意的是两个index都是u2类型的,那么就表明它们的值分别为00 06 和00 13,换成十进制就是6和19。
这个分析完了,这也就是我们第一个常量池中常量方法的分析了。 来看第二个:

对,是09,我们查表:

跟常量method属性一致,连个值分别为20,21,这里就不分析了,下一个:

对,一下子到这里来,因为上面的都差不多,先查tag值,然后查询数据结构,看自己的Index所占的字节,然后获取数据,然后下一个。 这个是01,那就是字符串了:

这次是length,看到结果是个00 0C,那么就是12了,那么就意味着后面的12个字节就是这个字符串的内容了,好了,看一下吧:
|
1
2
3
|
43 4F 55 4E 54 5F 4E 55 4D 42 45 52转义为COUNT_NUMBER |
那么也就是我们自定义的常量字符了。 好了,到了这里我就不分析了,因为下去都一样了,其实Javap给我们提供了这种功能:
|
1
|
javap -verbose ClassName |
看图:

从这个图里,我们可以得到我们关于class的基本信息了,有次版本主版本,还有今天得常量池,看我们分析的也基本上和它一样了。
好了,今天就记录在这里吧,刚刚开始,感觉有些难,不知不觉写了这么多,其实理解起来不算很难吧。
也参考过老大的PPT,还有一些书籍,算了不写上去了,累啊。。。
看懂class文件 转的更多相关文章
- 一个故事看懂Linux文件权限管理
前情回顾: 我通过open这个系统调用虫洞来到了内核空间,又在老爷爷的指点下来到了sys_open的地盘,即将开始打开文件的工作. 详情参见:内核地址空间大冒险:系统调用 open系统调用链 我是一个 ...
- 看懂Class文件的装载流程
Class文件的加载过程 ClassLoader的工作模式 类的热加载 1 Class文件的装载流程 只有被java虚拟机装载的Class类型才能在程序中使用(注意装载和加载的区别) 1.1 类装载的 ...
- 一图看懂hadoop分布式文件存储系统HDFS工作原理
一图看懂hadoop分布式文件存储系统HDFS工作原理
- 看懂Oracle执行计划
最近一直在跟Oracle打交道,从最初的一脸懵逼到现在的略有所知,也来总结一下自己最近所学,不定时更新ing- 一:什么是Oracle执行计划? 执行计划是一条查询语句在Oracle中的执行过程或访问 ...
- 看懂SqlServer查询计划【转】
原文链接:http://www.cnblogs.com/fish-li/archive/2011/06/06/2073626.html 开始 SQL Server 查找记录的方法 SQL Server ...
- 文科生也能看懂的iptables教程(转载)
据说还是个MM, 写得很通俗易懂, 还很诙谐, 原文:http://dallascao.com/cn/iptables-tutorial-for-newbies/ 对于斗胆开始玩vps的文科生来讲,i ...
- PHP笔记——java程序员看懂PHP程序
PHP笔记——java程序员看懂PHP程序 php是一种服务器端脚本语言,类型松散的语言. <?php ?> xml风格 <script language=”ph ...
- 【转载】看懂SqlServer查询计划
看懂SqlServer查询计划 阅读目录 开始 SQL Server 查找记录的方法 SQL Server Join 方式 更具体执行过程 索引统计信息:查询计划的选择依据 优化视图查询 推荐阅读-M ...
- 看懂SqlServer查询计划
看懂SqlServer查询计划 阅读目录 开始 SQL Server 查找记录的方法 SQL Server Join 方式 更具体执行过程 索引统计信息:查询计划的选择依据 优化视图查询 推荐阅读-M ...
随机推荐
- leetcode 852. Peak Index in a Mountain Array
Input: [0,1,0] Output: 1 Input: [0,2,1,0] Output: 1解: 比较数组中的i和i-1的大小,如果前一位大于后一位数字,前一位则是结果 let ans = ...
- Linux free -m 详解命令
如下显示free是显示的当前内存的使用,-m的意思是M字节来显示内容.我们来一起看看. 1 2 3 4 5 6 $ free -m total used ...
- linux 下的ssh免密登陆设置
一,原理 说明: A为linux服务器a B为linux服务器b 每台linux都有ssh的服务端和客户端,linux下的ssh命令就是一个客户端 我们常用ssh协议来进行登陆或者是文件的拷贝,都需要 ...
- py库: Selenium (自动化测试)
http://blog.csdn.net/liujingqiu/article/details/50458553 http://www.cnblogs.com/zhaof/p/6953241.html ...
- 《算法》第四章部分程序 part 6
▶ 书中第四章部分程序,加上自己补充的代码,图的环相关 ● 无向图中寻找环 package package01; import edu.princeton.cs.algs4.In; import ed ...
- 输出1到n以内的素数
package cn.lhj.learn; /** * 输出1~n以内的素数 * * @author lhj * */ public class TestSuShu { public static v ...
- 01.hadoop集群环境搭建
hadoop集群搭建的步骤 1.安装jdk2修改ip地址3.关闭防火墙4.修改hostname5.设置ssh自动登陆6.安装hadoop-------------------------------- ...
- git fail to push some refs....
出现错误的主要原因是github中的README.md文件不在本地代码目录中 可以通过如下命令进行代码合并[注:pull=fetch+merge] git pull --rebase origin m ...
- 图文详解AO打印(端桥模式)(转)
一.概述 AO打印是英文Active-Online Print的简称,也称主动在线打印.打印前支持AO通讯协议的AO打印机首先通过普通网络与C-Lodop服务保持在线链接,网页程序利用JavaSc ...
- springboot 停止
因springboot内嵌tomcat或jetty使得我们没法去操作服务: 因此,常常是服务起来后,要重启时会端口占用,我们只能无情的kill掉端口. 不过spring也设置有配置停止的请求: App ...