《深入Java虚拟机学习笔记》- 第6章 class文件

一、class文件内容

Java class文件是对Java程序二进制文件格式的精确定义。每一个Java
class文件都对一个Java类或者Java接口作出了全面描述。一个class文件只 能包含一个类或接口;

class文件内容按顺序如下(ClassFile表中各项简介如下):

  1. magic(魔数)-u4
    • 0xCAFEBABE,其作用在于可以轻松辨别出Java class文件和非Java class文件;

  2. minor_version-u2和major_version-u2(次、主版本号)
    • 对于虚拟机来说,主次版本号确定了特定的class文件格式,通常只有给定主版本号和一系列次版本号后,虚拟机才能读取该class文件;

    • 如下图所示

      第5、6字节表示次版本号=0
      第7、8字节表示主版本号=50(高位在前)

  3. constant_pool_count-u2和constant_pool(常量池)
    • 常量池的数量为constant_pool_count-1,其索引从1开始;

    • 除直接常量外,常量池还包括类和接口的全限定名、字段的名称和描述符以及方法的名称和描述符等符号引用;

    • 每个常量池入口都有一个字节的标志,这个标志指出了列表中该位置的常量类型。虚拟机获取这个标志后,就知道在标志后面的常量类型了;其中每一
      个标志都有一个相对应的表结构来描述该常量池;具体类型如下表所示:

  4. access_flags(访问标志)-u2
    • 访问标志展示了文件中类或接口的如下信息: 标志名 值 设置后的含义





















      ACC_PUBLIC 0x0001 public类型
      ACC_FINAL 0x0010 类为final类型
      ACC_SUPER 0x0020 使用新型的invokespecial语义
      ACC_INTERFACE 0x0200 接口类型,不是类类型
      ACC_ABSTRACT 0x0400 abstract类型

      附:invokespecial和invokevirtual
      invokespecial指静态绑定后,由JVM产生调用的方法。如super(),以及super.someMethod(),都属于
      invokespecial;
      invokevirtual指动态绑定后,由JVM产生调用的方法。如obj.someMethod(),属于invokevirtual;
      正是由于这两种绑定的不同,在子类覆盖超类的方法、并向上转型引用后,才产生了多态以及其他特殊的调用结果。


  5. this_class-u2(当前类)
    • 它是一个对常量池的索引,在this_class位置的常量池入口必须为CONSTANT_Class类型对应的表结构
      CONSTANT_Class_info...;

    • 下图显示了常量池的使用方法:

      在CONSTANT_Utf8_info表的bytes里存放的即是当前类的全限定名字符串;

  6. super_class-u2(直接超类)
    • 和this_class表示的内容相同;

    • 除了java.lang.Object类以外,常量池索引super_class对于所有类均有效;

    • 对于java.lang.Object,其super_class为0;

    • 对于接口,其super_class指向的常量池为java.lang.Object;

  7. interfaces_count-u2和interfaces(接口列表)
    • 保存该类直接实现或该接口所扩展的接口数量;

    • 接口按implements子句和extends子句出现的顺序显现;

    • interfaces表中的每一项都是对父接口的常量池的索引,该索引用CONSTANT_Class_info来描述;

    • 如果接口数为0,则没有interfaces表;

  8. fields_count-u2和fields(字段列表)
    • 只有在class文件中由类或接口声明了的字段才在fields列表中列出,不列出从超类或父接口中继承而来的字段;

    • fields列表可能会包含在对应的Java源文件中没有叙述的字段,这是因为Java编译器可能会在编译时向类或者接口添加字段;如对一个内部类的fields列表来说,为了保持对外围类实例的引用,Java编译器会为每个外围类实例添加实例变量 ;

    • 第个field_info表包含以下字段信息:
      • 字段名字

      • 字段描述符和修饰符

      • 如果该字段被声明为final,则还包含其常量值;

  9. methods_count-u2和methods(方法列表)
    • methods_count表示该类或接口中所声明的所有方法的总计数,不包括超类或都父接口中继承来的方法;

    • methods列表中包含以下信息:
      • 方法名和描述符(方法的返回值类型和参数类型)

      • 如果方法即不是抽象的,也不是本地的,则还包含方法局部变量所需的栈空间长度、为方法所捕获的异常表、字节码序列以及可选的行数和局部变量

      • 如果方法能够抛出任何已验证异常,则methods列表就会包括一个关于这些已验证异常的列

  10. attributes_count-u2和attributes(属性列表)
    • class文件中最后的部分是属性,它给出了该文件中类或接口所定义的属性的基本信息;

    • 每个attribute_info表的第一项是指向常量池中CONSTANT_Utf8_info表的索引,该表给出了属性的名称;

class文件内容举例

  1. 代码 
    public interface MyInterface {
        void
    hello();
    }

  2. 编译后其字节码如下图:


    • 常量池数"0009"表示后面紧接着有8个常量池项

    • 常量池索引1表示一个CONSTANT_Class_info表(07),它引用索引为7的常量池;

    • 常量池索引1表示一个CONSTANT_Class_info表(07),它引用索引为8的常量池;

    • 常量池索引3表示一个CONSTANT_Utf8_info表(01),其内容为hello(方法名);

    • 常量池索引4表示一个CONSTANT_Utf8_info表(01),其内容为()V(方法参数与返回值);

    • 常量池索引5表示一个CONSTANT_Utf8_info表(01),其内容为SourceFile(某属性值);

    • 常量池索引6表示一个CONSTANT_Utf8_info表(01),其内容为MyInterface.java(某属性值);

    • 常量池索引7表示一个CONSTANT_Utf8_info表(01),其内容为MyInterface,被常量池索引1引用到(当前类);

    • 常量池索引8表示一个CONSTANT_Utf8_info表(01),其内容为java/lang/Object,被常量池索引2引用到
      (超类);

    • 访问标志"0601"表示是public(0001)、abstract(0400),且是接口(0200);

    • 当前类索引号"0001"表示指向常量池索引1,指明当前类为MyInterface;

    • 超类索引号"0002"表示指向常量池索引2,指明超类为java/lang/Object;

    • 实现的接口数"0000"表示没有实现任何接口;

    • 字段数"0000"表示该接口没有字段;

    • 方法数"0001"表示接口有一个方法;

    • 剩下的字节码为方法列表及属性列表;

二、特殊字符串


全限定名

常量池指向类或者接口时,给出的是全限定名,形如java/util/Hashtable;

简单名称

字段名和方法名以简单名称形式出现在常量池入口中,如上例中的常量池索引3中的"hello";

描述符

  1. 指向字段和方法的符号引用还包含描述符字符串;字段的描述符给出了字段的类型,方法描述符给出了方法的返回值和方法参数的数量、类型以及顺序;

  2. 描述符使用上下文无关语法:斜体表示非终结符,等宽字体表示终结符;

  3. 如下所示:

  4. 基本类型终结符 终结符 类型

























    B byte
    C char
    D double
    F float
    I int
    J long
    S short
    Z boolean

  5. 描述符举例 描述符 说明













    I int i;
    [Ljava/lang/Object; java.lang.Object[] obj;
    ([BII)Ljava/lang/String; String method(byte[] b, int i, int j)
    ZILjava/lang/String;II()Z boolean method(boolean b, int i, String s, int
    j, int k)

时间: 2024-10-09 00:00:33

《深入Java虚拟机学习笔记》- 第6章 class文件的相关文章

《深入Java虚拟机学习笔记》- 第4章 网络移动性

Java虚拟机学习笔记(四)网络移动性 <深入Java虚拟机学习笔记>- 第4章 网络移动性,布布扣,bubuko.com

《深入Java虚拟机学习笔记》- 第7章 类型的生命周期

一.类型生命周期的开始 如图所示 初始化时机 所有Java虚拟机实现必须在每个类或接口首次主动使用时初始化: 以下几种情形符合主动使用的要求: 当创建某个类的新实例时(或者通过在字节码中执行new指令,或者通过不明确的创建.反射.克隆和反序列化): 当调用某个类的静态方法时(即在字节码中执行invokestatic指令): 当使用某个类或接口的静态字段,或者对该字段赋值时(用final修饰的静态字段除外,它被初始化为一个编译时常量表达式): 当调用Java API中的某些反射方法: 当初始化某个

《深入Java虚拟机学习笔记》- 第13章 逻辑运算

<深入Java虚拟机学习笔记>- 第13章 浮点运算 <深入Java虚拟机学习笔记>- 第13章 逻辑运算,布布扣,bubuko.com

《深入Java虚拟机学习笔记》- 第12章 整数运算

Java虚拟机提供几种进行整数算术运算的操作码,他们执行基于int和long类型的运算.当byte.short和char类型值参与算术运算时,首先会将它们转换为int类型.这些操作码都不会抛出异常,溢出在这里通常可以被忽略. 整数加法 操作码 操作数 说明 iadd (无) 从栈中弹出两个int类型数,相加,然后将所得int类型结果压回栈 ladd (无) 从栈中弹出两个long类型数,相加,然后将所得long类型结果压回栈 将一个常量与局部变量相加 操作码 操作数 说明 iinc vindex

《深入Java虚拟机学习笔记》- 第2章 平台无关

Java虚拟机学习笔记(二)平台无关 <深入Java虚拟机学习笔记>- 第2章 平台无关,布布扣,bubuko.com

《深入Java虚拟机学习笔记》- 第14章 浮点运算

<深入Java虚拟机学习笔记>- 第13章 浮点运算

《深入Java虚拟机学习笔记》- 第19章 方法的调用与返回

<深入Java虚拟机学习笔记>- 第19章 方法的调用与返回

《深入Java虚拟机学习笔记》- 第5章 Java虚拟机

一.JVM的生命周期 当启动一个Java程序时,一个Java虚拟机实例就诞生了:当该程序关闭退出时,这个Java虚拟机也就随之消亡: JVM实例通过调用某个初始类的main方法来运行一个Java程序:这个main方法必须是public.static的,而且返回值必须是void:任何一个拥有这样的main方法的类都可以作为Java程序运行的起点: Java程序初始类中的main方法,将作为该程序初始线程的起点,其它任何线程都是由这个初始线程启动的: 守护线程和非守护线程 守护线程通常是由虚拟机自己

《深入Java虚拟机学习笔记》- 第17章 异常

<深入Java虚拟机学习笔记>- 第17章 异常

《深入Java虚拟机学习笔记》- 第18章 finally子句

本章主要介绍字节码实现的finally子句.包括相关指令以及这些指令的使用方式.此外,本章还介绍了Java源代码中finally子句所展示的一些令人惊讶的特性,并从字节码角度对这些特征进行了解释. 1.微型子例程 字节码中的finally子句表现的很像"微型子例程".Java虚拟机在每个try语句块和与其相关的catch子句的结尾处都会"调用"finally子句的子例程.finally子句结束后(这里的结束指的是finally子句中最后一条语句正常执行完毕,不包括抛