class文件是什么?? 是二进制文件,是被jvm识别的二进制文件。这是Java初学者的回答。

较为深入的学习者,可能会给出跨语言编程的概念和实现,class文件就是一个跨语言的实现的第一步,也是动态修改整理已完成编码的代码,去生成新代码的指令的划时代编程的第一步(动态类生成技术)。

动态生成类技术:

class文件是一种8位字节的二进制流文件, 各个数据项按顺序紧密的从前向后排列, 相邻的项之间没有间隙, 这样可以使得class文件非常紧凑, 体积轻巧, 可以被JVM快速的加载至内存, 并且占据较少的内存空间。 我们的Java源文件, 在被编译之后, 每个类(或者接口)都单独占据一个class文件, 并且类中的所有信息都会在class文件中有相应的描述, 由于class文件很灵活, 它甚至比Java源文件有着更强的描述能力。
class文件中的信息是一项一项排列的, 每项数据都有它的固定长度, 有的占一个字节, 有的占两个字节, 还有的占四个字节或8个字节, 数据项的不同长度分别用u1, u2, u4, u8表示, 分别表示一种数据项在class文件中占据一个字节, 两个字节, 4个字节和8个字节。 可以把u1, u2, u3, u4看做class文件数据项的“类型” 。
class文件中存在以下数据项(该图表参考自《深入Java虚拟机》):
类型 名称 数量
u4 magic 1
u2 minor_version 1
u2 major_version 1
u2 constant_pool_count 1
cp_info constant_pool constant_pool_count - 1
u2 access_flags 1
u2 this_class 1
u2 super_class 1
u2 interfaces_count 1
u2 interfaces interfaces_count
u2 fields_count 1
field_info fields fields_count
u2 methods_count 1
method_info methods methods_count
u2 attribute_count 1
attribute_info attributes attributes_count

下面对class文件中的每一项进行详细的解释。fields(域,字段或者信息组,包含方法和属性)

class文件中的魔数和版本号

(1) magic

在class文件开头的四个字节, 存放着class文件的魔数, 这个魔数是class文件的标志,他是一个固定的值: 0XCAFEBABE 。 也就是说他是判断一个文件是不是class格式的文件的标准, 如果开头四个字节不是0XCAFEBABE, 那么就说明它不是class文件, 不能被JVM识别。

(2)minor_version 和 major_version

紧接着魔数的四个字节是class文件的此版本号和主版本号。 随着Java的发展, class文件的格式也会做相应的变动。 版本号标志着class文件在什么时候, 加入或改变了哪些特性。 举例来说, 不同版本的javac编译器编译的class文件, 版本号可能不同, 而不同版本的JVM能识别的class文件的版本号也可能不同, 一般情况下, 高版本的JVM能识别低版本的javac编译器编译的class文件, 而低版本的JVM不能识别高版本的javac编译器编译的class文件。 如果使用低版本的JVM执行高版本的class文件, JVM会抛出java.lang.UnsupportedClassVersionError 。具体的版本号变迁这里不再讨论, 需要的读者自行查阅资料。

class文件中的常量池概述

在class文件中, 位于版本号后面的就是常量池相关的数据项。 常量池是class文件中的一项非常重要的数据。 常量池中存放了文字字符串, 常量值, 当前类的类名, 字段名, 方法名, 各个字段和方法的描述符, 对当前类的字段和方法的引用信息, 当前类中对其他类的引用信息等等。 常量池中几乎包含类中的所有信息的描述, class文件中的很多其他部分都是对常量池中的数据项的引用,比如后面要讲到的this_class, super_class, field_info, attribute_info等, 另外字节码指令中也存在对常量池的引用, 这个对常量池的引用当做字节码指令的一个操作数。  此外, 常量池中各个项也会相互引用。

class文件中的项constant_pool_count的值为1, 说明每个类都只有一个常量池。 常量池中的数据也是一项一项的, 没有间隙的依次排放。常量池中各个数据项通过索引来访问, 有点类似与数组, 只不过常量池中的第一项的索引为1, 而不为0, 如果class文件中的其他地方引用了索引为0的常量池项, 就说明它不引用任何常量池项。class文件中的每一种数据项都有自己的类型, 相同的道理,常量池中的每一种数据项也有自己的类型。 常量池中的数据项的类型如下表:

常量池中数据项类型 类型标志 类型描述
CONSTANT_Utf8 1 UTF-8编码的Unicode字符串
CONSTANT_Integer 3 int类型字面值
CONSTANT_Float 4 float类型字面值
CONSTANT_Long 5 long类型字面值
CONSTANT_Double 6 double类型字面值
CONSTANT_Class 7 对一个类或接口的符号引用
CONSTANT_String 8 String类型字面值
CONSTANT_Fieldref 9 对一个字段的符号引用
CONSTANT_Methodref 10 对一个类中声明的方法的符号引用
CONSTANT_InterfaceMethodref 11 对一个接口中声明的方法的符号引用
CONSTANT_NameAndType 12 对一个字段或方法的部分符号引用

每个数据项叫做一个XXX_info项, 比如, 一个常量池中一个CONSTANT_Utf8类型的项, 就是一个CONSTANT_Utf8_info 。除此之外, 每个info项中都有一个标志值(tag), 这个标志值表明了这个常量池中的info项的类型是什么, 从上面的表格中可以看出, 一个CONSTANT_Utf8_info中的tag值为1, 而一个CONSTANT_Fieldref_info中的tag值为9 。

Java程序是动态链接的, 在动态链接的实现中, 常量池扮演者举足轻重的角色。 除了存放一些字面量之外, 常量池中还存放着以下几种符号引用:

(1) 类和接口的全限定名

(2) 字段的名称和描述符

(3) 方法的名称和描述符

在详细讲解常量池中的各个数据项之前, 我们有必要先了解一下class文件中的特殊字符串, 因为在常量池中, 特殊字符串大量的出现,这些特殊字符串就是上面说的全限定名和描述符。 要理解常量池中的各个数据项, 必须先了解这些特殊字符串。

class文件中的特殊字符串

首先说明一下, 所谓的特殊字符串出现在class文件中的常量池中, 所以在上一篇博客中, 只是对常量池介绍了一个大概。 本着循序渐进和减少跨度的原则, 首先把class文件中的特殊字符串做一个详细的介绍, 然后再回过头来继续讲解常量池。 
在上文中, 我们提到特殊字符串是常量池中符号引用的一部分, 至于符号引用的概念, 会在以后提到。 现在我们将重点放在特殊字符串上。 特殊字符串包括三种: 类的全限定名, 字段和方法的描述符, 特殊方法的方法名。 下面我们就分别介绍这三种特殊字符串。

(1) 类的全限定名

在常量池中, 一个类型的名字并不是我们在源文件中看到的那样, 也不是我们在源文件中使用的包名加类名的形式。 源文件中的全限定名和class文件中的全限定名不是相同的概念。 源文件中的全新定名是包名加类名, 包名的各个部分之间,包名和类名之间, 使用点号分割。 如Object类, 在源文件中的全限定名是java.lang.Object 。 而class文件中的全限定名是将点号替换成“/” 。 例如, Object类在class文件中的全限定名是 java/lang/Object 。 如果读者之前没有接触过class文件格式, 是class文件格式的初学者, 在这里不必知道全限定名在class文件中是如何使用的, 只需要知道, 源文件中一个类的名字, 在class文件中是用全限定名表述的。 

(2) 描述符

我们知道在一个类中可以有若干字段和方法, 这些字段和方法在源文件中如何表述, 我们再熟悉不过了。 既然现在我们要学习class文件格式, 那么我们就要问, 一个字段或一个方法在class文件中是如何表述的? 在本文中, 我们会讨论方法和字段在class文件中的描述。 方法和字段的描述符并不会把方法和字段的所有信息全都描述出来, 毕竟描述符只是一个简单的字符串。 
在讲解描述符之前, 要先说明一个问题, 那就是所有的类型在描述符中都有对应的字符或字符串来对应。 比如, 每种基本数据类型都有一个大写字母做对应, void也有一个大写字符做对应。 下表是void和基本数据类型在描述符中的对应。
基本数据类型和void类型 类型的对应字符
byte B
char C
double D
float F
int I
long J
short S
boolean Z
void V
基本上都是以类型的首字符变成大写来对应的, 其中long和boolean是特例, long类型在描述符中的对应字符是J, boolean类型在描述符中的对应字符是Z 。 
基本类型和void在描述符中都有一个大写字符和他们对应, 那么引用类型(类和接口,枚举)在描述符中是如何对应的呢? 引用类型的对应字符串(注意, 引用类型在描述符中使用一个字符串做对应) , 这个字符串的格式是:
“L” + 类型的全限定名 + “;”  
注意,这三个部分之间没有空格, 是紧密排列的。 如Object在描述符中的对应字符串是: Ljava/lang/Object;  ; ArrayList在描述符中的对应字符串是: Ljava/lang/ArrayList;  ; 自定义类型com.example.Person在描述符中的对应字符串是: Lcom/example/Person; 。
我们知道, 在Java语言中数组也是一种类型, 一个数组的元素类型和他的维度决定了他的类型。 比如, 在 int[] a 声明中, 变量a的类型是int[] , 在 int[][] b 声明中, 变量b的类型是int[][] , 在 Object[] c 声明中, 变量c的类型是Object[] 。既然数组是类型, 那么在描述符中, 也应该有数组类型的对应字符串。 在class文件的描述符中, 数组的类型中每个维度都用一个 [ 代表, 数组类型整个类型的对应字符串的格式如下:
若干个“[”  +  数组中元素类型的对应字符串  
下面举例来说名。 int[]类型的对应字符串是: [I ;int[][]类型的对应字符串是: [[I ;Object[]类型的对应字符串是: [Ljava/lang/Objec;Object[][][]类型的对应字符串是: [[[Ljava/lang/Object。
介绍完每种类型在描述符中的对应字符串, 下面就开始讲解字段和方法的描述符。 
字段的描述符就是字段的类型所对应的字符或字符串。 如: int i 中, 字段i的描述符就是 I ;Object o中, 字段o的描述符就是 Ljava/lang/Object;double[][] d中, 字段d的描述符就是 [[D 。 
方法的描述符比较复杂, 包括所有参数的类型列表和方法返回值。 它的格式是这样的:
(参数1类型 参数2类型 参数3类型 ...)返回值类型  
其中, 不管是参数的类型还是返回值类型, 都是使用对应字符和对应字符串来表示的, 并且参数列表使用小括号括起来, 并且各个参数类型之间没有空格, 参数列表和返回值类型之间也没有空格。 
下面举例说明(此表格来源于《深入Java虚拟机》)。
方法描述符 方法声明
()I int getSize()
()Ljava/lang/String; String toString()
([Ljava/lang/String;)V void main(String[] args)
()V void wait()
(JI)V void wait(long timeout, int nanos)
(ZILjava/lang/String;II)Z boolean regionMatches(boolean ignoreCase, int toOffset, String other, int ooffset, int len)
([BII)I int read(byte[] b, int off, int len )
()[[Ljava/lang/Object; Object[][] getObjectArray()

(3) 特殊方法的方法名

首先要明确一下, 这里的特殊方法是指的类的构造方法和类型初始化方法。 构造方法就不用多说了, 至于类型的初始化方法, 对应到源码中就是静态初始化块。 也就是说, 静态初始化块, 在class文件中是以一个方法表述的, 这个方法同样有方法描述符和方法名。

类的构造方法的方法名使用字符串 <init> 表示, 而静态初始化方法的方法名使用字符串 <clinit> 表示。 除了这两种特殊的方法外, 其他普通方法的方法名, 和源文件中的方法名相同。

读《深入jvm原理》之class文件相关推荐

  1. J2EE JVM加载class文件的原理机制

    JVM加载class文件的原理机制 1.Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中 2.java中的 ...

  2. JVM加载class文件的原理机制

    阅读目录 1.JVM 简介 2.JVM 的组成部分 3.JVM加载class文件的原理机制 在面试java工程师的时候,这道题经常被问到,故需特别注意. 回到顶部 1.JVM 简介 JVM 是我们Ja ...

  3. 常见Java面试题之JVM加载class文件的原理机制

    JVM加载class文件的原理机制 JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的 ...

  4. 描述一下JVM加载class文件的原理机制

    Java中的所有类,都需要由类加载器装载到JVM中才能运行.类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中.在写程序的时候,我们几乎不需要关心类的加载,因为这些都是隐式装载的 ...

  5. java文件读入原理_描述一下JVM加载class文件的原理机制

    1.JVM 简介 JVM 是我们Javaer 的最基本功底了,刚开始学Java 的时候,一般都是从"Hello World "开始的,然后会写个复杂点class ,然后再找一些开源 ...

  6. Java提高篇——JVM加载class文件的原理机制

    阅读目录 1.JVM 简介 2.JVM 的组成部分 3.JVM加载class文件的原理机制 在面试java工程师的时候,这道题经常被问到,故需特别注意. 回到顶部 1.JVM 简介 JVM 是我们Ja ...

  7. Java 虚拟机(JVM)原理介绍

    Java 虚拟机[JVM]原理介绍 1.概述 2.Java类的加载原理机制 2.1 .Java类的加载过程 2.2 .Class loader (类加载器) 2.2.1 类的生命周期 2.2.1.1 ...

  8. 什么是JVM?深入解析JVM原理!

    一.JVM是什么? JVM是Java Virtual Machine(Java虚拟机)的缩写,是通过在实际的计算机上仿真模拟各种计算机功能来实现的.由一套字节码指令集.一组寄存器.一个栈.一个垃圾回收 ...

  9. 读懂 JVM 内存管理这篇就够了

    读懂 JVM 内存管理这篇就够了 JVM 的内存结构 程序计数器 作用 概述 PC寄存器的常见问题 虚拟机栈 栈中可能出现的异常 栈的存储单位 栈运行原理 栈帧的内部结构 局部变量表 槽 Slot 操 ...

  10. 三代测序原理与数据文件简介(SMRT+Nanopore)

    三代测序原理与数据文件简介(SMRT+Nanopore) 一生雾梦 2019-12-03 20:48:42  1578  收藏 2 分类专栏: 前沿文献分析 文章标签: 三代测序(SMS) SMRT  ...

最新文章

  1. JSP 客户端请求概述
  2. seo说_百度指数看世间沉浮_如何快速排名-互点快速排名_网站SEO关键词排名优化提升技巧...
  3. torch==1.1.0和torchvision-0.3.0安装
  4. Spark on K8S 的最佳实践和需要注意的坑
  5. matlab swt函数,matlab swt 函数出错
  6. Coding Interview Guide -- 向有序的环形单链表中插入新节点
  7. java lucene cms_JEECMSv6标签使用之[@cms_lucene_list]
  8. AutoPlay Menu Builder入门教程
  9. poi html转换成word文档,poi将html转换为word文档
  10. java银行叫号_银行排队叫号系统
  11. 简单的C语言实训代码
  12. KVM实验二 pepsi-wyl
  13. 广发样样行提额技巧分享
  14. leetcode954.二倍数对数组C++(绝对值排序)
  15. 利用Matlab对Excel数据表参数进行频谱分析(FFT)的方法
  16. win10安装程序无法正常启动
  17. 技术分享 | 语音AI如何驱动虚拟人
  18. PHP笔记 17 18 19 20 21
  19. 免费网络营销与推广的几种方法
  20. 软件方法(下)分析和设计第9章分析 之 分析类图——案例篇(20211228更新)

热门文章

  1. 不要再次进行阅读的计算机论文与理由(持续更新中)
  2. error: missing or invalid dependency detected while loading class file 'RDD.class'.
  3. 机器学习(十八)——关联规则挖掘
  4. HTML5中本地数据库(SQLLite)的基础
  5. 推荐一款ui架构--frozenui
  6. 大话javascript 2期:执行上下文与执行上下文栈
  7. form表单回车提交问题
  8. 【转】log4net使用详解
  9. 大型企业网络配置系列课程详解(四) --HSRP和VRRP配置与相关概念的理解(一)...
  10. jquery简洁遮罩插件