深入理解Java虚拟机读书笔记十一

第 章 类文件结构

6.1.无关性的基石

     Java虚拟机提供的语言无关性

《深入理解Java虚拟机读书笔记十一》

6.2. Class类文件的结构

    Class文件是以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在 Class 文件之中,中间没有添加任何分隔符,这使得整个class文件中存储的内容几平全部都是程序运行的必要数据,没有空隙存在。

    Cl ass文件格式:无符号数和表。

    无符号数属于基本的数据类型,以 u I 、 u2 、 u4、 u8 来分别代表 个字节、 个字节、个字节和 个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值,或者按照UTF-8 编码构成字符串值。

    表是由多个无符号数或其他表作为数据项构成的复合数据类型,所有表都习惯性地

以“info”结尾。表用于描述有层次关系的复合结构的数据,整个 Cl ass 文件本质上就一张表。

《深入理解Java虚拟机读书笔记十一》

6.2.1.魔数与 Class 文件的版本

每个Cl ass文件的头4个字节称为魔数 ( Magic Number ),它的唯一作用是用于确定这个文件是否为一个能被虚拟机接受的Class文件。

很多文件存储标准中都使用魔数,来进行身份识别,譬如图片格式。使用魔数而不是扩展在来进行识别主要是基于安全考虑,因为文件扩展各可以很随意地被改动。Class文件的魔数的获得很有 ” 浪漫气息,值为OxCAFEBABE

6.2.2.常量池

常量池是Class文件结构中与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一,同时它还是在Class文件中第一个出现的表类型数据项目。

制定Class文件格式规范时,将第0项常量空出来是有特殊考虑的,这样做是为满足后面某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池项目 ”。

Class文件结构中只有常量池的容量计数是从1开始的,对于其他集合类型从0开始的。

常量地之中 主要存放两大量常量:

     字面量 ( Litcral ) 和符号引用 ( Symbo1icReferences)

字面量:类似Java常量概念,如文本字符串、被声明为final的常量值。

符号引用:属于编译原理方面的概念。

     三类常量:

         类和接口的全限定名 ( Fully Qualified Name)

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

         方法的名称和描述符

虚拟机加载Class文件的时候进行动态连接:在Class文件中不会保存各个方法和字段的最终内存布局信息,因此这些字段和方法的符号引用不经过转换的话是无法直接被虚拟机使用的。当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析并翻译到具体的内存地址之中。

常量池中的每一项常量都是一个表,共有11种结构各不相同的表结构数据,有一个共同的特点:表开始的第一位是一个u1类型的标志位,缺少2的标志位。

《深入理解Java虚拟机读书笔记十一》

6.2.3.类索引、父类索引与接口索引集合

    类索引 (this_class) 和父类索引(super_class) 都是一个u2 类型的数据,而接口索引集合 ( interfaces ) 是一组 u2类型的数据的集合,Class文件中由这三项数据来确定这个类的继承关系。

    类索引引用于确定这个类的全限定名,父类索引引用于确定这个类的父类全限定名。由于Java语言不允许多重继承承,所以父类牵引只有一个,除了java.lang.Object之外,所有的Java类都有父类,因此除了java.lang.Object 外,所有Java类的父类索引都不为0

    接口索引集合就用来描述这个类是实现了哪些接口,这些被实现的接口将按implements 语句(如果这个类本身是一个接口,则应当extends语句)后的接口顺序从左到有排列在接口的索引集合中。

《深入理解Java虚拟机读书笔记十一》

6.3.4.字段表集合

    字段表(field_info) 用于描述接口或类中声明的变量。字段( field ) 包括了类级变量或实例级变量, 但不包括在方楼内部声明的变量。

包括:

     字段的作用域 (public 、 private 、 protected修饰符)、是类级变量还是实例级变量(static 修饰符 、可变性 ( final ) 、并发可见性( volatile 修饰符,是否强制从主内存读写)、可否序列化 ( transient 修饰符、字段数据类型 基本类型、对象、数组、字段名称。

各个修饰符都是布尔值

《深入理解Java虚拟机读书笔记十一》

《深入理解Java虚拟机读书笔记十一》

    在实际情况中,ACC_PUBLlC、 ACC_PRIVATE、 ACC_PROTECTED 三个标志最多只能选择其一。 ACC_FlNAL、 ACC_VOLATILE 不能同时选择。接口之中的字段必须有 ACC_PUBLlC、 ACC_STATIC 、 ACC_FINAL 标志,这些都是由 Java 本身的悟言规则所决定的。

    name_index 和 descriptor_ index 对常量池的引用,分别代表着字段的简单名称及字段和方法的描述符。

《深入理解Java虚拟机读书笔记十一》

     对于数组类型,每一维度将使用个前置的“”字将来描述,如一个定义为“java.lang.String[]”类型的二维数组,将被记录为“[ [ Ljava.lang.String;”,一个整型组“int[]”将被记录为“[I”。

     字段表集合中不会列出从超类或父类继承而来的字段,但有可能列出版本Java代码之中不存在的字段,譬如在内部类中为了保持对外部类访问性会自动添加指向外部类实例的字段。

6.3. 5.方法表集合

    方法表集合和字段表集合结构类似

《深入理解Java虚拟机读书笔记十一》

    方法见面的代码去哪里了?

    方法里的Java代码经过编译器编译成字节码指令之后,存放在方法属性表集合中,一个名为“Code”的属性里面,属性表作为Class文件格式中最具扩展性的一种数据项目。

    如果父类方法在子类中没有被重写 (override) ,方发表集合中就不会出现来自父类的方法信息。

    在Java语言中,要重载(override)一个方法,除了要与方法具有相同的简单名称之外,还必须有一个与原方法不同的特征签名。

    特征签名就是一个方法中各个参数在常量池中的字段符号引用的集合,也就是因为返回值不会包含在特征签名之中,因此Java语言里面是无法仅仅依靠返回值的不同来对一个已有方法进行重载的。

6.3.6.属性表集合

《深入理解Java虚拟机读书笔记十一》

《深入理解Java虚拟机读书笔记十一》

1 .Code属性

    为字节码指令存储在 Code属性内, Code属性出现在方法表的属性集合之中,但并非所有的方法都必须存在这个属性。譬如接口或抽象类中的方法就不存在 Code属性。

《深入理解Java虚拟机读书笔记十一》

code_length 代表字节码长度,code是用于存储字节码指令的一系列字节流。

    Çode属性是 Class 文件中最重要的一个属性,如果把一个Java文件中的信息分为代码 ( Code方法体里面的Java代码)和元数据( metadata,包括类、字段、方法定义及其他信息)两部分,那么在整个Class文件里,Code属性用于描述代码,所有的其他数据项目用于描述元数据。

2.Exceptions属性

    Exception属性的作用是列举出方法中可能抛出的受查异常,也就是方法描述时在throws关键字后面列举的异常。

3.LineNumberTable属性

    LineNumberTable属性用于描述Java源码行号与字节码行号(字节码的偏移量)之间的对应关系。可以在Javac中使用 g:none或 g:lines选项采取消或要求生成这项情息。

如果选择不生成 LineNurnberTable属性,对程序.运行产生的.主要的影响就是在抛出异

常时,堆栈中将不会显示出错的行号,并且在调试程序的时候无法按照源码来设置断点。

4.LocalVariableTable属性

    LocalVariableTab1e属性用于描述栈锁中局部变量表中的变量与Java源码定义的变量之间的关系,它不是运行时必需的属性,默认也不会生成到Class文件之中,可以在Javac中使用 g:none 或 g : vars选项来取消或要求生成这项信息。

5.SourceFile属性

    SourceFile属性用于记录生成这个Class文件的源码文件名称。这个属性也是可选的,可以使用javac-g:none-g:source选项来关闭或要求生成这项信息。 在Java中,对于大多数的类来说,类名和文件名是一致的,但是有一些特殊情况(内部类)例外。如果不生成这项属性,当抛出异常时,堆栈中将不会显示出错误代码所属的文件名。

6.ConstantValue属性

    ConstantValue属性的作用是通知虚拟机自动为静态变量赋值。只有被static关键字修饰的变量(类变量)才能可以使用这项属性。

    对于非static类型的变量(也就是实例变量)的赋值是在实例构造器<init>方法中进行的;而对于类变量,则有两种方式可以选择:赋值在类构造器<clinit>方法中进行,或者使用ConstantValue属性来赋值。目前Sun Javac编译器的选择是如果同时使用finalstatic来修饰一个变量(或者说常量更贴切),并且这个变量的数据类型是基本类型或java.lang.String的话,就生成ConstantValue属性来进行初始化,如果这个变量没有被final修饰,或者并非基本类型及字符串,则选择在<clinit>方法中进行初始化。

7.lnnerClasses 属性

    InnerÇlas属性用于记录内部类与宿主类之间的关联。如果一个类中定义了内部类,那编译器将会为它及它所包含的内部类生成InnerClasses属性。

8.DeprecatedSynthetic属性

    Deprecated 和 Synthetic两个属性都属于标志类型的布尔属性, 只存在有和没有的区别,没有属性值的概念。

    Deprecated属性用于表示某个类、字段或方法,已经被程序作者定为不再推荐使用,它可以通过在代码中使用@deprecated注释进行设置。

    Synthetic属性代表此字段或方法并不是由Java源码直接产生,而是由编译器自行添加的,标识一个类、字段或方法是由编译器自动产生的。

    原文作者:java虚拟机
    原文地址: https://blog.csdn.net/yinxiangbing/article/details/29570949
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞