“利用碎片化的时间,为你精讲局部知识。”
少废话,先来张图看看一个Class文件长什么样
package com.test;
public class TestClass {
private int m;
public int inc(){
return m+1;
}
}
以TestClass.java为例
Javac TestClass.java
编译后,使用WinHex打开:
1、学习Class文件的必要性:
了解Class文件结构以及能够分析Class文件中每个字节码代表的含义,这对于我们了解JAVA Virtual Machine(Java虚拟机)很有帮助。
2、学习的引导:
刚接触的读者遇到二进制格式的.Class文件概念时会有畏难情绪,其实,Class文件规则真的很简单。
所有格式的文件都有其对应的文件格式,什么位置放置什么长度的什么值的字节,这些都是有统一的规定。JDK为了实现在新版本下也能执行旧版本的class文件,所以会让Class文件结构保持稳定。因此,不用担心学习的速度跟不上技术的更新。
3、Class文件的基础知识
Class文件是二进制文件,8位组成一个字节,也就是8 bits 组成 1 byte,比如00010001这8位,由于二进制并不直观,我们使用16进制来表示,11110001二进制可表示成 F1十六进制,这样更加直观。
Class文件包含“ 无符号数 ” 和“ 表 ” 。没理解没关系,到示例中你就会明白了。
无符号数:可以用来描述数字、索引引用、数量值、按照UTF-8编码构成的字符串值。常有的长度分别是u1(1个字节)、u2(2个字节)、u4(4个字节)、u8(8个字节)。
表:可由无符号数和其他表组成。这是一种递归的定义方式,打个比方:个人信息表单中包含了一条一条的信息,好比电话号码、姓名等,同时也可以包含一张亲属联系方式的联系方式表,这样应该就好理解了。
4、Class文件其实是一张表
如下图:
分析上面这张图;第一列是类型,代表是无符号数还是表,以_info结尾的那一行是表,其余的是无符号数。第二列是名称,代表无符号数和表的具体名称,第三列是数量,无符号数都是一个,表的个数可以是0个或1个及以上,数量一般取决与前一个无符号数的值。
分别看一下名称的含义:(子表的结构没有放出来,之后会详细介绍)
名称 | 含义 |
---|---|
Magic | 魔数,是一种文件标识符、值固定,用来区别于非Class文件。 |
Minir_version | 次版本号,很少使用,很少更改,一般也是固定的值。 |
Major_version | 主版本号,编译环境的jdk版本决定,有规律。 |
Constant_pool_count | 常量池中常量的个数 |
Constant_pool | 常量池表,特别注意的是他的数量是1,只是它里面包含的常量数是Constant_pool_count个。 |
Access_flag | 访问标志,通过这个标志位可以判断该类的权限大小以及是类还是接口还是其他类型。 |
This_class | 存放的本类的常量池引用(常量池引用对应的也就是对应的全限定类名,下同)。 |
Super_class | 存放的父类的常量池引用。Interface_count 继承的接口个数。 |
Interface_count | 继承的接口个数。 |
Interfaces | 继承的接口表,有多少个继承的接口就有多少个表,此表的结构后头介绍。 |
Fields_count | 类中字段的个数 |
Fileds | 类中字段表,同样,有多少个类字段就有多少个表,此表的结构后头介绍。 |
Methods_count | 类中方法的个数 |
Methods | 类中方法表,同样,有多少个类方法就有多少个表,此表的结构后头介绍。 |
Attributes_count | 属性表个数 |
Attributes | 属性表,这里的属性并不存在于类中,而是将一些很长的信息统一归为各种属性放在最后,以便于前面的那些表引用这里。让布局更加整齐一些。 |
接下来我们就拿一个实列来体验一下吧。
前4个字节就是魔数:CAFEBABE,固定的。
接着的2个字节代表次版本号,一般固定位00 00。
接着的2个字节代表主版本号,我用的是JDK8版本,16进制的34对应10进制的52,JVM规范中规定JDK 1.1版本对应主版本号是45,JDK每增加一个版本则加1。
接着的2个字段代表常量池中常量的个数。
常量池中含有什么我们不得而知,我们可以通过cmd来查看javap工具为我们解析的常量池:
Javap -verbose TestClass.class
常量池多是一些字符引用、字面量、类方法接口的符号引用名。
下面看一下常量池表的结构规定如下图:
需要说明的是:标志,占用u1(1个字节),通过告诉读取器,接下来这个常量是什么类型。
比如0A是10进制的10,就是CONSTANT_Methodref_Info常量,每个常量又有自己不同的表结构。CONSTANT_Methodref_Info表包括u1标志、u2类索引、u3方法索引。解析一下上图就是:u1标志(0A)、u2类索引(00 04)、u3方法索引(00 0F),结合之前cmd得出的常量池4索引代表java/lang/Object,16索引代表"": ()V。这个方法是编译器自己添加的,感兴趣的读者可以自己查阅下资料,或者评论区讨论。
常量池的解析就是上面的步骤。下面我贴出常量池中各子表的结构,读者可继续按照我说的步骤自己操作一遍。
后续未完成的部分将留作下次讲解。
尾巴:以上知识点融入了自己的理解,如果有疑问或不足,欢迎评论区或私信讨论。
参考文献:
《深入理解Java虚拟机:JVM高级特性与最佳实践(第三版)》
作者:周志明
ISBN:9787111641247
转载:https://blog.csdn.net/liangcheng0523/article/details/106463181