带你看看Java虚拟机的Class文件

495次阅读  |  发布于2年以前

今天聊聊Class文件,也就是字节码文件的一些知识,保准新手小白也能看懂,只要不走神……

什么是Class文件

首先大家要搞清楚,什么是Class文件,我们看一个代码,比如这里有一个Java源文件,内容是:

public class TestDemo {
   public static void main(String[] args) {
       System.out.println("hello");
  }
}

这没什么问题吧?此时这个文件存放在我的桌面:

此时我们打开命令行工具,输入以下指令:

此时就会在我们的桌面生成一个新的文件:

这就是Class文件!同时我们一般把它叫做字节码文件,这才是真正实现跨平台的东西,到了这里你就需要明白,Class文件和字节码文件是一回事!

那什么是字节码文件:

所谓的字节码文件就是Java源代码经过编译器(javac)编译之后生成的新的文件,也就是一个后缀为.class的文件,此文件是一种二进制的类文件,它的内容是JVM指令,此文件就是字节码文件!

ok,以上就是对字节码文件的解读!

那到了这里,肯定就会有人比较好奇,这个字节码文件里面是什么内容啊,我们可以使用文本工具将其打开看一下:

一般,我们用常规的文本编辑工具直接打开,它是无法识别的,比如这里我们就要打开它:

然后你看的就是这些,啥也不是,完全看不懂,一堆乱码,怎么办?(虽然我们看不懂,但是JVM看得懂)

一般来说我们会将其以**十六进制的形式打开**,如下:

看到的就是这样的,其实你还是会觉得看不懂,没关系,我们后面就会着重去分析这些都是什么意思,也就是搞清楚这个字节码文件到底是个啥?

怎么去查看Class文件

我们上面对Class文件也即字节码文件有了基本的认识,那么现在我们怎么去查看这个Class文件呢?上面我们说了,Class文件如果我们用一般的文本编辑器去打开的话,是乱码,什么也看不懂,通常情况下,我们以十六进制的形式展现

也就是上述这个样子,也即是说,对于Class文件,我们直接打开显示的是这样子的:

看不懂啊,就好比你不会英语,给你一段英语,你看不懂啊,怎么办?翻译啊,可以将其翻译成中文你不就看懂了嘛,同样的,对于原始的Class文件,我们直接打开是看不懂的,怎么办?也是翻译啊,翻译成啥?通常翻译成十六进制的形式,也即是这样:

这样看就友好多了,当然,它是无法直接翻译成中文的,只能翻译到这了,那接下来我们就需要去解读这些十六机制代码都是什么含义,也就是去解读这个Class文件内容,这是我们的重点!

那这里先给大家介绍下,如何去查看这个Class文件,我用的工具叫做“010 Editor”(只要是十六进制文本编辑器都可),觉得还是很强大,很方便的,直接打开Class文件,这里可以进行转换:

而且在这里可以进行更多格式的切换:

比如我们将其以二进制形式展现:

可以看到,二进制直接展现看着也是比较懵的,还是十六进制的形式比较友好,所以这个工具推荐给大家,已给大家准备好安装包,除此之外大家还可以使用比较常见的编辑工具比如notpad++或者是sublime等,这些工具都可以以十六进制形式展示文件内容!

OK,我们看到的这些十六进制形式的内容可以说是Class文件的原版本,还有一些工具可以直接将Class文件进行解读以后将内容展现给我们,比如有这么一款工具(学习JVM必备:https://github.com/ingokegel/jclasslib)。叫做“JClasslib”,这就是一个专门的字节码查看器,我们用它打开Class文件:

这里展示的信息就是对Class文件进行解读之后的,更加方便我们去看,去分析,这款工具有Windows客户端,同时我们可以在IDEA中安装这款工具插件:

安装完这个插件之后,我们将代码进行编译之后,就可以点击View:

打开该工具就会在右边显示该Class文件内容解读:

ok,目前就给大家介绍这些方式,后面我们会用到这些去给大家分析Class文件的内容!

这里再给大家强调一下,可以这么说,原汁原味的Class文件也即是字节码文件是这样子的

我们要分析的也是这个!OK!

分析十六进制文件

接下来我们就来分析一下这个全是数字和字母的十六进制文件,这个就是Class文件了,也叫作字节码文件,我们看看这些东西该怎么解读!

首先清楚,这个字节码文件中大致包含如下几部分:

  1. 魔数
  2. Class文件版本
  3. 常量池
  4. 访问标志
  5. 索引集合
  6. 字段表集合
  7. 方法表集合
  8. 属性表集合

接着我们以常量池为例来分析一个案例,让你理解这个字节码文件的分析是怎样的~

首先要知道,常量池也是存储数据的一个结构,它的结构如下:

{
 u2         constant_pool_count;
 cp_info    constant_pool[constant_pool_count - 1]
}

这个u2就是Class文件中的一个数据类型,代表2字节大小,所以常量池就包含了两部分:

  1. 常量池的大小,也就是常量池计数器(常量池包含常量池计数器),比如为5,真正的常量池索引为1-4,因为0索引被保留了
  2. 常量池项集合,就是具体的常量池中的各个内容了,也就是字面量和符号引用这些

而对于不同的字面量和符号引用,都还有各自的一个类型,比如同为字面量的整数6和字符串6,但是前者类型是int后者类型是String,所以不同的数据有不同的类型,别看常量池主要保存的就是字面量和符号引用,但是拆开仔细一看,这两者包含的数据一共有17种类型(截止jdk13),如下:

我们以CONSTANT_Integer_info为例,它就代表我们常见的int类型,每一个常量项同时也是有两部分组成,通用表示如下:

cp_info{
 u1 tag; //表示类型
 u1 info[]; //表示内容的字节数组
}

比如这个常量项“CONSTANT_Integer_info”,我们常见的int类型就属于这个常量项,看它的组成就是:

CONSTANT_Integer_info{
 u1 tag;
 u4 bytes[];
}

这是怎么回事呢?比如我们这里写的Java代码(源java文件):

public class TestDemo {
   public final int a = 6;
   public static void main(String[] args) {
       System.out.println("hello");
  }
}

这里我们定义了一个final整型常量a,值为“6”,那这里的这个“6”就是一个字面量,是要放在常量池中的,对应的常量池结构就是CONSTANT_Integer_info,而CONSTANT_Integer_info包含两个部分,首先是占1字节的tag就是一个u1大小的类型,这个数值固定是3,然后就是占4个字节的具体存储字面量数值的字节数组info了!

我们目前查看Class文件是以16进制查看的,那么这里的字面量“6”是十进制,需要将其转换成十六进制放进常量池,十六进制为“0x06”:

那这个十进制的字面量“6”在常量池中的布局结构就如下所示:

这个时候我们再查看上述举例的源Java文件生成的Class文件,也就是十六机制的文件:

可以发现,这个字面量6正如咱们分析的一样出现在了字节码Class文件当中!这就将我们写的java代码中的变量与这个Class文件中的内容对应了起来!

当然,上述只是举了一个例子,实际上Class文件的内容很多,分析起来也比加多复杂,以上只是让大家简单了解一下!

如果想系统全面的学习,可以关注下咱们的Java就业手册,持续更新中……

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8