文章整体目录

  • 前言
  • 1、魔数
  • 2、版本号
  • 3、常量池
    • 3.1、常量池大小
    • 3.2、常量池内容
  • 4、类的访问控制权限
  • 5、类名
  • 6、接口
    • 6.1、实现的接口个数
    • 6.2、接口内容
  • 7、属性
    • 7.1、属性的数量
    • 7.2、属性的值
  • 8、成员方法
    • 8.1、成员方法数量
    • 8.2、成员方法的值
  • 9、额外属性
    • 9.1、额外属性的数量
    • 9.1、额外属性的值

前言

  我们知道java文件在经过编译之后就会生成字节码文件,即.class文件。随后.class文件就会被对应类加载器加载到内存中。那么是不是存在这么一种可能,我自创一种编程语言,叫做jvav,然后使用自己的编译器将其编译成为JVM所识别的.class文件,是不是也可以运行在JVM中呢?答案是肯定的。
  其实运行在JVM之上的编程语言不止Java一种,还有像groovy、kotlin、scala这些语言都是运行在JVM之上的,这些不同的语言的语法肯定是不同的,但是这些语言在经过各自的编译器进行了编译之后生成的.class文件都是按照相同的规则进行生成的。

  我们研究.class文件可以对JVM的类加载过程有一个更加清晰的认识。下面我就带大家看看一个.class文件到底是长什么样的。

提示:
  接下的内容会使用到一个idea插件叫做 jclasslib 去查看字节码文件。

首先说明一个.class文件的整体结构,如下图所示:

提示:
  上图中,每个方框前面的小方框表示该内容的大小。u2表示占2字节、u4表示占4字节、!表示大小不定。

然后我们通过一个简单的代码示例来演示我们接下来的内容:

public class Test {private int a = 10;public void method(String a) {}
}

这段代码编译了之后的.class文件,我们使用16进制的方式来打开,内容如下:

现在看起来这一堆16进制内容感觉是晦涩难懂的。别急,我们一块块内容的来解析。

1、魔数

  • 每个Class文件的头4个字节称为魔数(Magic Number)
  • 唯一作用是用于确定这个文件是否为一个能被虚拟机接受的Class文件。
  • Class文件魔数的值为0xCAFEBABE。如果一个文件不是以0xCAFEBABE开头,那它就肯定不是Java class文件。

很多文件存储标准中都使用魔数来进行身份识别,譬如图片格式,如gif或jpeg等在文件头中都存有魔数。在.class文件中的表示如下:

2、版本号


在魔数后面的四个字节存储的是class文件的版本号。5、6字节是次版本号(Minor Version);7、8两个字节是主版本号(Major Version)。版本号的对照表如下:

主版本号与次版本号在.class文件中的表示如下:

可以看到主版本号值为0x0034,对应十进制为52。次版本号为0,因此对应JDK版本就是1.8。

3、常量池

常量池分为两个部分存储。第一部分为常量池大小,第二部分为常量池内容

3.1、常量池大小

常量池大小是使用2个字节进行表示的,因此常量池中最大的元素个数可以表示到65536。.class文件中的常量池大小表示:

0x0017所表示的十进制数为23,我们再使用jclasslib观察常量池的内容:

可以看到其编号是从1开始的,因此只有22个常量。因此实际的常量个数为.class文件中的表示个数-1。

3.2、常量池内容

class中的常量池一共有11种类型,每种类型都有一个tag标识其种类,每种类型的tag都只占1字节。如下图所示:

观察我们的字节码文件,可以发现常量池的第一个值为 0X0a,其十进制表示为10。根据上表我们可以看出其类型为CONSTANT_Methodref_info,tag后面还有两个index属性。第一个index属性为指向声明方法的类描述符CONSTANT_Class_info的索引项,占据两字节;第二个index属性为指向名称及类型描述符CONSTANT_NameAndType_info的索引项,占据两个字节。其在.class文件中的表示如下:

第一个index所表示的16进制为0x0004,其十进制为4;第二个index所表示的16进制为0x0013,其十进制为19。
再观察jclasslib中的字节码文件,tag 10表示的是 CONSTANT_Methodref_info,可以发现常量池中第一个常量就是 CONSTANT_Methodref_info,其后有两个index,分别指向4和19(其实就是符号引用)。如下图:

按照这样的顺序,就可以解析完毕常量池中所有的常量。这里就不再赘述,直接开始下一个部分。

4、类的访问控制权限

类的访问控制权限占据2个字节

其修饰符标志如下图所示:

在.class文件中,标志如下:

可知其为0x0021。0x0021的原因是因为该类的访问修饰符为0x0001 | 0x0020 = 0x0021得到的。

观察jclasslib可以发现其对应的就是public权限:

5、类名

类名分为两部分:本类的类名和父类的类名。这两部分都分别占据两个字节。

本类的类名以及父类类名的字节码表示为:

可知本类类名的16进制为0x0003,对应十进制为3。父类类名的16进制为0x0004,对应十进制为4。观察常量池中第三与第四个常量,如下:

6、接口

接口分为两个部分,第一部分为实现的接口个数,占据2个字节;第二部分为实现的接口。

6.1、实现的接口个数

占用四个字节,因此最多能够实现的接口个数为 65535 个接口。观察.class字节码文件。可以发现值为0,因为我们并没有实现任何接口。

6.2、接口内容

一旦接口的个数为0,那么接口内容就会为空。

7、属性

属性也分为两个部分,第一部分为属性的数量,占据2个字节;第二部分为属性的值。

7.1、属性的数量

占用四个字节,因此一个类的成员属性最多也是 65535 个。0x0001表示只有一个属性。

7.2、属性的值

字段访问权限标记:

字段描述符解释表:

属性在class文件中的存储数据结构:

第一个字段access_flags占据两个字节;第二个字段name_index占据两个字节;第三个字段descriptor_index(描述符)占据两个字节;第四个字段attributes_count(属性数量,因为属性中还会有属性)占据两个字节;第五个字段attributes[attributes_count] 属性内容(如果属性的数量为0,那么这块区域在class文件中也不会存在)。

1、access_flags:0x0002(观察第一张表可知,表示 private)
2、name_index:0x0005(指向常量池,表示属性名)

3、descriptor_index:0x0006(指向常量池,表示属性类型)
这里的值为I,观察第二张表可知,表示为int。

4、attributes_count:0x0000(该属性中没有属性,因此这里长度为0)
5、attributes[attributes_count](由于attributes_count为0,因此这里没有内容)。其数据结构为:

8、成员方法

方法也分为两个部分,第一部分为方法的数量,占据2个字节;第二部分为方法的值。

8.1、成员方法数量

使用两个字节表示成员方法的数量,因此最大的成员方法数量也会65535。0x0002表示该类中含有两个方法。

但是可以看到类中只写了1个method方法,这是因为还有一个隐藏着的默认构造方法。(如果该类中含有静态属性,那么在编译时还会生成一个clinit方法,用于给静态字段初始化)

8.2、成员方法的值

首先说一下方法的组成结构。一个Method由Code和Exception组成,如下图:

方法访问权限和属性标志:

方法的数据结构,与属性的数据结构是一致的:

第一个字段access_flags占据两个字节;第二个字段name_index占据两个字节;第三个字段descriptor_index(描述符)占据两个字节;第四个字段attributes_count(属性数量,包括属性和异常)占据两个字节;第五个字段attributes[attributes_count] 属性内容(如果属性的数量为0,那么这块区域在class文件中也不会存在)。观察示例的class文件:

1、access_flags:0x0001(观察第一张表,表示 public)
2、name_index:0x0007(指向常量池,< init >表示构造方法)

3、descriptor_index:0x0008(指向常量池)
这里值为()V。()表示该方法没有参数;V表示返回值为void

4、attributes_count:0x0001(该方法中只一个属性,由于没有异常因此该属性就是Code)
5、attributes[attributes_count]:(这个就是表示Code)
code的数据结构如下:

u2 attribute_name_index: 00 09(指向常量池,表示Code)


u4 attribute_length: 00 00 00 39(十进制数为57,表示从这里开始到这个方法结束总共会占据57个字节)


u2 max_stack(操作数栈大小): 00 02

u2 max_locals(局部表大小): 00 01

可知操作数栈和局部变量表的大小是在编译时期就能够知道了。

u4 code_length: 00 00 00 0b
方法体或者说字节码指令的大小,这里是11个字节

u1 code(方法体或者说字节码指令): 2ab7 0001 2a10 0ab5 0002 b1(一共11个字节)

对应的就是这六个指令,执行引擎所执行的就是这里的内容。

u2 exception_length:00 00(此例中不存在异常,因此这里的长度就为0,所以异常的区域这里就不存在)

u2 attribute_count: 00 02(这个就是Code的属性,这里数量为2,表示其含有两个属性)
Code的第一个属性叫做LineNumberTable_attribute,就是行号。其数据结构如下:

u2 attr_name_index: 00 0a(对应十进制为10,这里就表示其为LineNumberTable的开始)

对应常量池内容为:

u4 attr_length: 00 00 00 0a(表示该属性的大小,这里占10个字节)

u2 line_number_table_length: 00 02(表示下面结构的个数,有两个这样的结构)

第一个结构的值
[
  u2 start_pc 00 00

  u2 line_number 00 02

]
第二个结构的值
[
  u2 start_pc 00 04

  u2 line_number 00 04

]
这两个结构在jclasslib中的表现就是:

Code的第二个属性叫做LocalVariableTable,就是局部变量表。其数据结构如下:

u2 attr_name_index: 00 0b (对应十进制就是11,这里就表示其为LocalVariableTable的开始)

对应常量池内容为:

u4 attr_length: 00 00 00 0c(对应十进制为12,表示该属性的大小,这里是占12个字节)

u2 table_length: 00 01(表示下面结构的个数,有一个这样的结构)

[
  u2 start_pc: 00 00

  u2 length: 00 0b(对应十进制长度就是11)

  u2 name_index: 00 0c(对应十进制就是12)

对应常量池属性就是:

  u2 descriptor_index: 00 0d(对应十进制就是13)

对应常量池属性就是:

以上对应的jclasslib中的表示就是:

  u2 index: 00 00

]
到此一个方法的解析就完毕了,方法的解析是整个class文件中最为复杂的。接下来的method方法的解析就省略了。

9、额外属性

额外属性也分为两个部分,一个是额外属性的数量,一个是额外属性的值。

9.1、额外属性的数量

使用两个字节表示额外属性的数量,因此最大的额外属性数量也会65535。0x0001表示该类中含有一个额外属性。

9.1、额外属性的值

额外属性在class文件中的存储数据结构:

u2 attr_name_index:0x0011

这里的值为0x0011,其十进制数为17,查看常量池中17对应的值(表示这里是SourceFile的开始):

u4 attr_length:0x0000 0002(表示该部分所占大小,这里为2字节)
u2 sourcefile_index:0x0012(对应十进制就是18)

对应常量池中的属性如下:

以上两属性对应jclasslib中表示如下:


到此一个简单的class文件就解析完毕了。其实可以发现,class文件就是按照一定的规则进行生成的,因此解析class文件也就按照这样的规则进行解析就好了。我们去了解class文件的就可以更加清晰地了解到类加载机制中类加载器是如何将一个class文件加载入内存中,同时也可以让我们对于class文件不再那么陌生

java之class文件解析相关推荐

  1. java实现Excel文件解析---apache POI以及把汉字转化为拼音

    java实现Excel文件解析----apache  POI以及把汉字转化为拼音 1.POI简介 Apache POI是Apache软件基金会的开放源码函式库,POI提供给Java程序对Microso ...

  2. java怎么xml文件解析_Java对Xml文件解析

    JAVA 解析 XML 通常有两种方式,DOM 和 SAX. DOM 虽然是 W3C 的标准,提供了标准的解析方式,但它的解析效率一直不尽如人意,因为使用DOM解析XML时,解析器读入整个文档并构建一 ...

  3. 基于Java的NetCDF文件解析

    近期在做的项目中,需要使用Java语言进行NetCDF文件的解析. 然而,当在寻找资料时,发现基于Java语言的资料相较于Python少了很多,而且现有的基于Java解析NetCDF文件到CSV的资料 ...

  4. java中 Excel文件解析及超大Excel文件读写

    本文主要对Excel中数据的解析和生成进行总结 前言 在应用程序的开发过程中,我们经常要用到Excel进行数据的导入或导出.所以,在通过Java语言实现此类需求时,通常会对Excel文件进行解析或生成 ...

  5. 【E文件解析】Java实现E文件解析为对象

    参照一位大佬贡献的代码修改的(扩展了对象解析,修改了一些bug,比如流异常未关闭) E语言解析包_e文件-Java代码类资源-CSDN下载 封装好了,三行代码解析为对象 github:  https: ...

  6. java ios乱码_相同的后台java代码,txt文件解析,安卓解析正常,IOS却是乱码,PC解析也正常.......

    中文乱码,之所以出现这种现象,根本原因是解析和编码所按照的字符集不 同,而字符集是什么呢? ``` 字符集(Character set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同, ...

  7. java sax xml文件解析_java解析xml文件-DOM/SAX

    java解析xml文件的两种方式 1:DOM 原理:把整个文档加载到内存,转化成dom树,之后应用程序可以随机的访问dom树的任何数据,灵活 快,但消耗内存 一个简单的xml 使用java解析 //b ...

  8. Java中的文件解析——Excel解析

    在日常的开发过程中,经常会使用到excel工作簿进行数据的保存,那么在java中,通常会使用第三方提供的技术来进行excel文件的解析,比如:Apache POI.JXL.Alibaba EasyEx ...

  9. JAVA中xml文件解析几种方式

    1. DOM解析xml /*** DOM解析xml* @param xmlPath xml文件路径*/public static void getInfoByDom(String xmlPath){D ...

最新文章

  1. 深度优先搜索_0基础学算法 搜索篇第一讲 深度优先搜索
  2. 用django将数据从数据库提出并分页展示
  3. 前Citrix CTO认为虚拟化将解决现有的安全问题
  4. 非递归遍历N-ary树Java实现
  5. LeetCode精讲 03无重复字符的最长子串(滑动窗口)
  6. 前端 <table><td><tr><th>
  7. html聚光灯特效,css实现聚光灯效果的代码分享
  8. Kotlin 势必取代 Java?
  9. linux批量重命名脚本,Linux批量命名文件SH脚本整理
  10. Flutter实战5 -- 天气查询APP重构之状态管理(ScopedModel)
  11. c语言怎样编写图形,「分享」C语言如何编写图形界面
  12. 合作的进化 6-10
  13. DDR March系列算法整理
  14. PHP刷步数,微信支付宝修改步数刷步源码/带卡密功能PHP程序
  15. Gitter:高颜值GitHub小程序客户端诞生记
  16. 第四次作业之四则运算
  17. 【2021-12-11】在 Windows 系统下,迁移 Android Studio 的 Android 虚拟设备(AVD)到非系统分区,释放系统盘空间
  18. Android 修改SIM卡默认VOLTE值
  19. sys 系统库 笔记(一)—— 简介与快速入门
  20. 机器学习算法[9]--集成方法之XGBoost原理详解及XGBoost库实现

热门文章

  1. 图片无损放大软件哪个好?图片放大不失真这样做
  2. 小米8ios图标包下载_小米Max2开发版刷机包(最新官方固件rom包下载)
  3. 收货地址 html,收货地址.html
  4. 对话系统最新进展-17篇EMNLP 2021论文
  5. Kevgir(Kevg)通关记录
  6. NIST宣布推出前4种抗量子加密算法
  7. 报错信息:java.io.FileNotFoundException拒绝访问
  8. 数学和计算机联系论文,数学与计算机论文
  9. 海洋cms泛目录系统
  10. [转]四大开源商业智能平台大比拼[http://database.ctocio.com.cn/analysis/289/7610289.shtml]