看懂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 ...
随机推荐
- vue2.0过滤器
最近一阶段,项目上比较清闲,有了更多的时间可以研究一下vue了. 这里记录一下关于vue2.0过滤器的学习. vue2.0删除了所有的框架自带的过滤器,也就是说,如果你在vue2.0当中想用过滤器,那 ...
- [java,2018-01-16] HttpClient发送、接收 json 请求
最近需要用到许多在后台发送http请求的功能,可能需要发送json和xml类型的数据. 就抽取出来写了一个帮助类: 首先判断发送的数据类型是json还是xml: import org.dom4j.Do ...
- Oracle 更改字符集 更改后之前的中文全成乱码了
安装时采用什么字符集主要看你的需求,一般测试用的话,就用UTF8.后面可以修改, 10g:UTF8 到ZHS16GBK SHUTDOWN IMMEDIATE; STARTUP MOUNT ; ALTE ...
- mongodb的Limit|skip|投影|排序|消除重复
Limit 方法limit():用于读取指定数量的文档 语法: db.集合名称.find().limit(NUMBER) 参数NUMBER表示要获取文档的条数 如果没有指定参数则显示集合中的所有文档 ...
- django之relacted.py(探秘django的关联field)
生成model类对象时,传入的每个field对象都会调用其contribute_to_class函数,生成对应的属性. def contribute_to_class(self, cls, name, ...
- PHP 扩展在 Linux(centos7)系统下的编译与安装 以 mysqli 为例
(操作系统 Centos7,环境版本 php7) 01,进入到 PHP 解压后的源码包的的 ext 文件夹 02,查看是否存在 mysqli 扩展 => ls, 如果不存在需要去响应网站下载 ( ...
- MVC登录校验
利用MVC自带的过滤器可现实简单的登录校验 在项目目录下创建一个BaseController控制器,让需要验证的控制器继承这个BaseController 需要让BaseController继承Con ...
- linux驱动开发第一步hello
先查看Ubuntu的版本 cat /etc/issue lin@lin-virtual-machine:~$ cat /etc/issue Ubuntu 12.04 LTS \n \l 或者使用 li ...
- C++中几种测试程序运行时间的方法<转>
转的地址:https://www.cnblogs.com/silentteen/p/7532855.html 1.GetTickCount()函数 原理: GetTickCount()是获取系统启动后 ...
- 如何使用navicat远程连接服务器上的oracle数据库