当前位置: 首页>编程语言>正文

java在class中单独编译一个文件 java编译后的class文件是几进制

前言:

class文件是一个16进制的文件,可以通过Hex Editor工具打开,打开后如下:

java在class中单独编译一个文件 java编译后的class文件是几进制,java在class中单独编译一个文件 java编译后的class文件是几进制_java,第1张

注:提供两个文档帮助我们阅读class文件

概述

我们先对这个class文件有个总体的了解

java在class中单独编译一个文件 java编译后的class文件是几进制,java在class中单独编译一个文件 java编译后的class文件是几进制_常量池_02,第2张

魔数

所有的由Java编译器编译而成的class文件的前4个字节都是“0xCAFEBABE”。(简称cafebabe-咖啡宝贝)
它的作用在于:当JVM在尝试加载某个文件到内存中来的时候,会首先判断此class文件有没有JVM认为
可以接受的“签名”,即JVM会首先读取文件的前4个字节,判断该4个字节是否是“0xCAFEBABE”,如
果是,则JVM会认为可以将此文件当作class文件来加载并使用。

版本号

主版本号和次版本号在class文件中各占两个字节,副版本号占用第5、6两个字节,而主版本号则占用

第7,8两个字节。

JDK版本号信息对照表:

java在class中单独编译一个文件 java编译后的class文件是几进制,java在class中单独编译一个文件 java编译后的class文件是几进制_java_03,第3张

注:有时候我们在运行程序时会抛出这个Error 错

误:“java.lang.UnsupportedClassVersionError: Bad version number in .class

file”。上面已经揭示了出现这个问题的原因,就是在于当前尝试加载class文件的JVM虚拟

机的版本 低于class文件的版本。

可以用当前虚拟机重新编译或者将当前虚拟机更新到和class文件一样的版本。

常量池计数器

它占两个字节,表示常量池中的个数。
常量池计数器默认从1开始而不是从0开始,把下标为0的空余出来

常量池数据区

常量池是class文件中非常重要的结构,它描述着整个class文件的字面量信息。常量池是由一组

constant_pool结构体数组组成的,而数组的大小则由常量池计数器指定。它的个数是常量池计数器的值-1。

那么常量池项中都存哪些数据呢?

java在class中单独编译一个文件 java编译后的class文件是几进制,java在class中单独编译一个文件 java编译后的class文件是几进制_java_04,第4张

那么常量池项 (cp_info)具体的结构是啥呢?

java在class中单独编译一个文件 java编译后的class文件是几进制,java在class中单独编译一个文件 java编译后的class文件是几进制_java在class中单独编译一个文件_05,第5张

JVM虚拟机规定了不同的tag值和不同类型的字面量对应关系如下:

java在class中单独编译一个文件 java编译后的class文件是几进制,java在class中单独编译一个文件 java编译后的class文件是几进制_jvm_06,第6张

int和float存储的结构如下:

java在class中单独编译一个文件 java编译后的class文件是几进制,java在class中单独编译一个文件 java编译后的class文件是几进制_java_07,第7张

long和double存储的结构如下:

需要注意的是long和double占8个字节,但是存储的时候是由两个4字节分开来的。这是受限于操作系统是32位导致的,32位就是4个字节,一次只能操作4个字节。

java在class中单独编译一个文件 java编译后的class文件是几进制,java在class中单独编译一个文件 java编译后的class文件是几进制_描述符_08,第8张

String类型存储的结构如下:

java在class中单独编译一个文件 java编译后的class文件是几进制,java在class中单独编译一个文件 java编译后的class文件是几进制_描述符_09,第9张

如上图所示的结构体,CONSTANT_String_info结构体中的string_index的值指向了

CONSTANT_Utf8_info结构体,而字符串的utf-8编码数据就在这个结构体之中,即tring_index的值hi某个CONSTANT_Utf8_info结构体在常量池中的索引。如下图所示:

java在class中单独编译一个文件 java编译后的class文件是几进制,java在class中单独编译一个文件 java编译后的class文件是几进制_java在class中单独编译一个文件_10,第10张

其中length表示这个utf-8编码的字节数组的长度,即有多少个字节。

byte[length]表示使用了utf-8编码后的具体字节数组。类文件中定义的类名和类中使用到的类的结构:

JVM会将某个Java 类中所有使用到了的类的完全限定名 以二进制形式的完全限定名 封装成

CONSTANT_Class_info结构体中,然后将其放置到常量池里。CONSTANT_Class_info 的tag值为 7

java在class中单独编译一个文件 java编译后的class文件是几进制,java在class中单独编译一个文件 java编译后的class文件是几进制_java在class中单独编译一个文件_11,第11张

其中 name_index的值是某个CONSTANT_Utf8_info结构体在常量池中的索引,对应的CONSTANT_Utf8_info结构体中存储了对应的二进制形式的完全限定名称的字符串,即类的全路径,但是会把".“换成”/",比如:

java在class中单独编译一个文件 java编译后的class文件是几进制,java在class中单独编译一个文件 java编译后的class文件是几进制_常量池_12,第12张

哪些字面量会进入常量池中?

  • final类型的8种基本类型的值会进入常量池。
  • 非final类型(包括static的)的8种基本类型的值,只有double、float、long的值会进入常量池。
  • 常量池中包含的字符串类型字面量(双引号引起来的字符串值)。

访问标志

访问标志,access_flags 是一种掩码标志,用于表示某个类或者接口的访问权限及基础属性。

java在class中单独编译一个文件 java编译后的class文件是几进制,java在class中单独编译一个文件 java编译后的class文件是几进制_jvm_13,第13张

类索引

类索引,this_class的值必须是对constant_pool表中项目的一个有效索引值。constant_pool表
在这个索引处的项必须为CONSTANT_Class_info 类型常量,表示这个 Class 文件所定义的类或接
口。

父类索引

父类索引,对于类来说,super_class 的值必须为 0 或者是对constant_pool 表中项目的一个有
效索引值。
如果它的值不为 0,那 constant_pool 表在这个索引处的项必须为CONSTANT_Class_info 类型常
量,表示这个 Class 文件所定义的类的直接父类。当前类的直接父类,以及它所有间接父类的
access_flag 中都不能带有ACC_FINAL 标记。对于接口来说,它的Class文件的super_class项的
值必须是对constant_pool表中项目的一个有效索引值。constant_pool表在这个索引处的项必须为
代表 java.lang.Object 的 CONSTANT_Class_info 类型常量 。
如果 Class 文件的 super_class的值为 0,那这个Class文件只可能是定义的是
java.lang.Object类,只有它是唯一没有父类的类。

接口计数器

接口计数器,interfaces_count的值表示当前类或接口的【直接父接口数量】。

接口信息数据区

接口表,interfaces[]数组中的每个成员的值必须是一个对constant_pool表中项目的一个有效索引
值, 它的长度为 interfaces_count。每个成员interfaces[i] 必须为
CONSTANT_Class_info类型常量,其中 【0 ≤ i <interfaces_count】。在interfaces[]数组
中,成员所表示的接口顺序和对应的源代码中给定的接口顺序(从左至右)一样,即interfaces[0]对
应的是源代码中最左边的接口。

字段计数器

字段计数器,fields_count的值表示当前 Class 文件 fields[]数组的成员个数。 fields[]数组
中每一项都是一个field_info结构的数据项,它用于表示该类或接口声明的【类字段】或者【实例字
段】。

字段信息数据区

字段表,fields[]数组中的每个成员都必须是一个fields_info结构的数据项,用于表示当前类或接
口中某个字段的完整描述。 fields[]数组描述当前类或接口声明的所有字段,但不包括从父类或父接
口继承的部分。

方法计数器

方法计数器, methods_count的值表示当前Class 文件 methods[]数组的成员个数。Methods[]
数组中每一项都是一个 method_info 结构的数据项。

方法信息数据区

方法表,methods[] 数组中的每个成员都必须是一个 method_info 结构的数据项,用于表示当前类
或接口中某个方法的完整描述。
如果某个method_info 结构的access_flags 项既没有设置 ACC_NATIVE 标志也没有设置
ACC_ABSTRACT 标志,那么它所对应的方法体就应当可以被 Java 虚拟机直接从当前类加载,而不需
要引用其它类。
method_info结构可以表示类和接口中定义的所有方法,包括【实例方法】、【类方法】、【实例初始
化方法】和【类或接口初始化方法】。
methods[]数组只描述【当前类或接口中声明的方法】,【不包括从父类或父接口继承的方法】。

属性计数器

属性计数器,attributes_count的值表示当前 Class 文件attributes表的成员个数。
attributes表中每一项都是一个attribute_info 结构的数据项。

属性信息数据区

属性表,attributes 表的每个项的值必须是attribute_info结构。

以上就是class文件中的信息。

特殊字符串字面量

特殊字符串包括三种: 类的全限定名, 字段和方法的描述符, 特殊方法的方法名

  • 类的全限定名
  • 描述符
    1.各类型的描述符
    基本数据类型(byte、char、double、float、int、long、short、boolean):除 long 和
    boolean,其他基本数据类型的描述符用对应单词的大写首字母表示。long 用 J 表示,
    boolean 用 Z 表示。
    void:描述符是 V。
    对象类型:描述符用字符 L 加上对象的全限定名表示,如 String 类型的描述符为Ljava/lang/String 。
    数组类型:每增加一个维度则在对应的字段描述符前增加一个 [ ,如一维数组 int[] 的描述
    符为 [I ,二维数组 String[][] 的描述符为 [[Ljava/lang/String 。
    2.字段描述符
    int i 中, 字段i的描述符就是 I
    Object o中, 字段o的描述符就是 Ljava/lang/Object;
    double[][] d中, 字段d的描述符就是 [[D
    3.方法描述符
    方法的描述符比较复杂, 包括所有参数的类型列表和方法返回值。 它的格式是这样的:
    (参数1类型 参数2类型 参数3类型 …)返回值类型
  • java在class中单独编译一个文件 java编译后的class文件是几进制,java在class中单独编译一个文件 java编译后的class文件是几进制_jvm_14,第14张

  • 特殊方法的方法名
    首先要明确一下, 这里的特殊方法是指的类的构造方法和类型初始化方法。
    构造方法就不用多说了, 至于类型的初始化方法, 对应到源码中就是静态初始化块。 也就是说,
    静态初始化块, 在class文件中是以一个方法表述的, 这个方法同样有方法描述符和方法名,具体如下:
    类的构造方法的方法名使用字符串 表示
    静态初始化方法的方法名使用字符串 表示。
    除了这两种特殊的方法外, 其他普通方法的方法名, 和源文件中的方法名相同。



https://www.xamrdz.com/lan/59x1960347.html

相关文章: