前言

现在周六公司进行一系列的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文件 转的更多相关文章

  1. 一个故事看懂Linux文件权限管理

    前情回顾: 我通过open这个系统调用虫洞来到了内核空间,又在老爷爷的指点下来到了sys_open的地盘,即将开始打开文件的工作. 详情参见:内核地址空间大冒险:系统调用 open系统调用链 我是一个 ...

  2. 看懂Class文件的装载流程

    Class文件的加载过程 ClassLoader的工作模式 类的热加载 1 Class文件的装载流程 只有被java虚拟机装载的Class类型才能在程序中使用(注意装载和加载的区别) 1.1 类装载的 ...

  3. 一图看懂hadoop分布式文件存储系统HDFS工作原理

    一图看懂hadoop分布式文件存储系统HDFS工作原理

  4. 看懂Oracle执行计划

    最近一直在跟Oracle打交道,从最初的一脸懵逼到现在的略有所知,也来总结一下自己最近所学,不定时更新ing- 一:什么是Oracle执行计划? 执行计划是一条查询语句在Oracle中的执行过程或访问 ...

  5. 看懂SqlServer查询计划【转】

    原文链接:http://www.cnblogs.com/fish-li/archive/2011/06/06/2073626.html 开始 SQL Server 查找记录的方法 SQL Server ...

  6. 文科生也能看懂的iptables教程(转载)

    据说还是个MM, 写得很通俗易懂, 还很诙谐, 原文:http://dallascao.com/cn/iptables-tutorial-for-newbies/ 对于斗胆开始玩vps的文科生来讲,i ...

  7. PHP笔记——java程序员看懂PHP程序

    PHP笔记——java程序员看懂PHP程序   php是一种服务器端脚本语言,类型松散的语言. <?php   ?>       xml风格 <script language=”ph ...

  8. 【转载】看懂SqlServer查询计划

    看懂SqlServer查询计划 阅读目录 开始 SQL Server 查找记录的方法 SQL Server Join 方式 更具体执行过程 索引统计信息:查询计划的选择依据 优化视图查询 推荐阅读-M ...

  9. 看懂SqlServer查询计划

    看懂SqlServer查询计划 阅读目录 开始 SQL Server 查找记录的方法 SQL Server Join 方式 更具体执行过程 索引统计信息:查询计划的选择依据 优化视图查询 推荐阅读-M ...

随机推荐

  1. linux安装python3 ,安装IPython ,安装jupyter notebook

    安装python3    下载到 /opt/中 1.下载python3源码,选择3.6.7因为ipython依赖于>3.6的python环境wget https://www.python.org ...

  2. linux进程端口防火墙

    进程端口: 1.netstat –apn :查看所有的进程和端口使用情况 2.查看8080端口是否被占用 [root@localhost bin]# lsof -i:8080 3.查看防火墙开放的端口 ...

  3. jsfiddle修改个人头像

    找了半天终于知道修改jsfiddle头像的方法了~ JsFiddle将Gravatar - 全球认可的头像用于个人资料图片.必须在这里改变你的头像,它也会在jsFiddle中自动更新. 注意,两者的注 ...

  4. TestLink 的使用详解

    二.初始配置(设置用户.产品) 1. 用户设置 在TestLink系统中,每个用户都可以维护自己的私有信息.admin可以创建用户,但不能看到其它用户的密码.在用户信息中,需要设置Email地址,如果 ...

  5. 关于SqlServer2008小记(查询数据库连接数,强行干掉连接)

    查询连接数 select count(*) from master.dbo.sysprocesses 这条语句查出来的是所有连接到本机(或者连接到本服务器)的连接数,并非是某一个库的连接数. 查询连接 ...

  6. c++引用(修改引用的值)

    当我们希望修改某个函数的返回值时,通常我们会返回这个值的引用(因为函数返回值其实是返回那个值得一份拷贝而已,所以想要修改必须使用引用): .h文件 #pragma once #include < ...

  7. 7.adb安装

    adb的全称为Android Debug Bridge,中文名“调试桥”顾名思义adb命令是调试手机中应用的一种方法,而且作为Android SDK中的工具,其功能非常强大,用这个命令行工具可以直接操 ...

  8. 分享给大家一个500G.Net ftp资料库

    把自己经常用到的一个ftp资料库分析给大家,大家可以到上面下载资料学习,资料比较齐全,另外还有部分工具, ftp的地址是:ftp://202.107.251.26

  9. Windows下卸载Apache、Mysql

    卸载Apache 1. 停止服务 2.以管理员身份打开命令环境 3. 删除Apache文件目录 卸载Mysql 一.在控制面板,卸载MySQL的所有组件控制面板——>所有控制面板项——>程 ...

  10. MySql Delete不走索引问题

    如果delete语句带有查询,写法不对会导致不走索引. 简单粗暴的办法:拆两条sql,一条查询,一条delete ======================= [不走索引的写法] DELETE FR ...