`
agapple
  • 浏览: 1597959 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Class文件

    博客分类:
  • java
阅读更多
一、Java Class文件是什么
《The JavaTM Virtual Machine Specification》(Second Edtion)中有表述:Java Class文件由8位字节流组成,所有的16位、32位和64位数据分别通过读入2个、4个和8个字节来构造,多字节数据总是按照Big-endian顺序来存放,即高位字节在前(放在低地址)。每个Class文件都包含且仅包含一个Java类型(类或者接口)。

或许,《The JavaTM Virtual Machine Specification》中的表述不够明确,那么我们可以参考一下《Inside the Java Virtual Machine》(Second Edtion)中的表述:Java Class文件特指以.class为后缀名的Java虚拟机可装载的文件。

分析一下两者的表述,我觉得都不够全面、不够明确。我是这么定义的:Java Class文件就是指符合特定格式的字节流组成的二进制文件。这个特定的格式就是指第二节要讨论的Class文件格式,亦即在《The JavaTM Virtual Machine Specification》中定义的Class文件格式。从另一个角度来说,这个特定格式就是指JVM能够识别、能够装载的格式。为什么这么说呢?因为JVM在装载class文件时,要进行class文件验证,以保证装载的class文件内容符合正确的内部结构。这个内部结构指的就是这个特定格式,只要是符合这个特定格式的Class文件都是合法的、规范的Class文件,都是JVM能够装载的Class文件。如果觉得这样的表述还是不够明确,我只能建议你读完这篇文章之后再回头来理解看看了J

为了讨论方便,在下文中将对这两个参考资料做个简记:

1)《The Java Virtual Machine Specification》(Second Edtion)简记为《JVM Spec》(2nded)。

2)《Inside the Java Virtual Machine》(Second Edtion) 简记为《Inside JVM》(2nded)。

二、Java Class文件的格式

在讲Class文件的格式之前,要介绍三个概念:

1)数据类型:《JVM Spec》(2nded)中指出,Java Class文件的数据用自己定义的一个数据类型集来表示,即u1,u2,u4,分别用于表示一个无符号类型的、占1,2,4个字节的数据。在《Inside JVM》(2nded)一书中,作者把这个数据类型集称之为Class文件的基本类型,本人觉得比较形象,便于理解。所以,在本文中,我们也用基本类型来表示Java Class文件的数据。

2)表:根据《JVM Spec》(2nded)中的定义,表(table)由项(定义见3)组成,用于几种Class文件结构中。《JVM Spec》(2nded)中指出,Java Class文件格式用一个类似于C结构的记号编写的伪结构来表示。这个伪结构指的就是这里的表,例如下面的ClassFile表就是这种伪结构的一个典型例子,下文中所有的表都是指这种伪结构的表。表的大小是可变的,这是因为它的组成部分项是可变的。注意;这里的可变是针对Class层次而言的,即在不同的Class文件中该项的大小可能不一样的,但是对于每一个具体的Class文件来说,这个项的大小又是一定的,因而这个表的大小也是一定的。那么,项为什么是可变的呢?请看下面的分析。

3)项:描述Java Class文件格式的结构的内容称为项(items)。每个项都有自己的类型和名称。项的类型可能是基本类型,也可能是一个表的名字,这种项都是一些数组项。数组项的每一个元素都是一个表,这个表同顶层的ClassFile表一样,也都是一种伪结构,也都是由一些项构成的,而且这些表不一定是同一种格式的,因此数组项也可以看作一个可变大小的结构流J。这些表对于该数组项来说就是子项,当然子项可能还有子项(目前子项的深度最多就两层)。项的名称,没有什么好说的,就是《JVM Spec》(2nded)中指定的一些名称。另外,项也是有大小的,对于没有子项的项来说,其大小是固定的;对于有子项的项来说,其大小是可变的。在一个具体的Class文件中,一个可变项(数组)的大小都会在其前一项中指定,为什么会是这样的呢?因为《JVM Spec》(2nded)中就是这么定义的!在Class文件中,每个项按规范中定义好的顺序存储在Class文件中,相邻的项之间没有任何间隔,连续的项(数组)也是按顺序存储,不进行填充或者对齐,这样可以使Class文件紧凑。

好了,我想这三个概念我已经解释地比较清楚了,下面开始正式解析Class文件的格式。

首先要来解析一下ClassFile表结构,这是《JVM Spec》(2nded)中定义的Class文件最外层的结构,换言之,就是Class文件的格式。


ClassFile表结构
    ClassFile {
        u4 magic;

        u2 minor_version;

        u2 major_version;

        u2 constant_pool_count;

        cp_info constant_pool[constant_pool_count-1];

        u2 access_flags;

        u2 this_Class;

        u2 super_Class;

        u2 interfaces_count;

        u2 interfaces[interfaces_count];

        u2 fields_count;

        field_info fields[fields_count];

        u2 methods_count;

        method_info methods[methods_count];

        u2 attributes_count;

        attribute_info attributes[attributes_count];

    }

ClassFile表结构由16个不同的项组成,其中的各项可以简要地分析如下:

(1) magic

每个Class文件的前4个字节被称为它的魔数(magic number): 0xCAFEBABE。魔数的作用在于:可以轻松地分辨出Java Class文件和非Java Class文件。(如果一个文件不是以0xCAFEBABE开头,它就肯定不是Java Class文件,因为它不符合规范J)。当Java还称为“Oak”的时候,这个魔数就已经定下来了,它预示了Java这个名字的出现。魔数的来历请大家自己查阅J

(2) minor_version和major_version

Class文件的下面4个字节包含了次、主版本号。通常只有给定主版本号和一系列次版本号后,Java虚拟机才能够读取Class文件。如果Class文件的版本号超出了Java虚拟机所能够处理的有效范围,Java虚拟机将不会处理该Class文件。例如J2SE5.0版本的虚拟机就不能执行由J2SE6.0版本的编译器编译出来的Class文件。

(3) constant_pool_count

版本号后面的项是constant_pool_count即常量池计数项,该项的值必须大于零,它给出该Class文件中常量池列表项的元素个数,这个计数项包括了索引为0的constant_pool表项,但是该表项不出现在Class文件的constant_pool列表中,因为它被保留为Java虚拟机内部实现使用了,因此常量池列表的元素个数constant_pool_count-1,各个常量池表项的索引值分别为1到constant_pool_count-1。

注:在这里,有几个术语需要解释一下,常量池即为constant_pool,常量池列表就是指constant_pool[ ],常量池表项即指常量池列表中的某一个具体的表项(元素)。这些常量池表项的可能类型如下述的cp_type表所示:

cp_type
入口类型                                标志值

CONSTANT_Class                           7

CONSTANT_Fieldref                        9

CONSTANT_Methodref                       10

CONSTANT_InterfaceMethodref              11

CONSTANT_String                           8

CONSTANT_Integer                          3

CONSTANT_Float                            4

CONSTANT_Long                             5

CONSTANT_Double                           6

CONSTANT_NameAndType                      12

CONSTANT_Utf8                              1


(4) constant_pool[ ]

constant_pool_count项下面是constant_pool[ ]项,即常量池列表,其中存储了该ClassFile结构及其子结构中引用的各种常量,诸如文字字符串、final变量值、类名和方法名等等。在Java Class文件中,常量池表项是用一个cp_info结构来描述的,常量池列表就是由constant_pool_count-1个连续的、可变长度的cp_info表结构构成的constant_pool[ ]数组。为什么是constant_pool_count-1个constant_pool的原因,在上面已经解释了。每一个常量池表项都是一个变长结构,其通常格式如下所示:

cp_info
cp_info {
        u1 tag;
        u1 info[];
    }

cp_info表的tag项是一个无符号的byte类型值,它表明了cp_info表的类型和格式,具体的tag类型见上表。

需要说明的是,cp_info只是一个抽象的概念,在Class文件中,它表现为一系列具体的、形如CONSTANT_Xxxx_info的constant_pool结构,其具体的格式由cp_info表的tag项(即第一个字节)来确定。不同的cp_info表,其info[]项也是不一样的,例如,CONSTANT_Class_info表的info[]项为“u2 name_index”,而CONSTANT_Utf8_info表的info[]项为“u2 length; u1 bytes[length];”,显然,这两个cp_info表是不一样的,大小更是不一样的,因而常量池表项的大小是可变的。由于常量池列表中的每个常量池表项的结构是不一样,因此常量池列表的大小也是可变的。在Class文件中,常量池列表项是一个可变长度的结构流。

由cp_info表以及cp_type表我们可以知道,若cp_info表中tag(标志)项的值为1时,当前的cp_info就是一个CONSTANT_Utf8_info表结构,若cp_info表中tag项的值为3,当前的cp_info就是一个CONSTANT_Integer_info表结构,其它情况类推。这些表的结构可以查阅《JVM Spec》(2nded)的第四章或者《Inside JVM》(2nded)的第六章。

(5) access_flags

紧接常量池后的两个字节称为access_flags,access_flags项描述了该Java类型的一些访问标志信息。例如,访问标志指明文件中定义的是类还是接口;访问标志还定义了在类或接口的声明中,使用了哪些修饰符;类和接口是抽象的还是公共的等等。实际上,access_flags项的值是Java类型声明中使用的访问标志符的掩码(mask,这里掩码指的是access_flags的值是所有访问标志值的总和,当然,未被使用的标志位在Class文件中都被设置为0。例如,若access_flags的值就是0x0001,就表示该Java类型的访问标志符是ACC_PUBLIC;若access_flags的值是0x0011,就表示该Java类型的访问标志符是ACC_PUBLIC和ACC_FINAL,因为只有这两个标志位的和才可能是0x0011;其它情况类推)。

一个Java类型的所有access_flags标志符如下表所示:



access_flags
标志名称         值           含义

ACC_PUBLIC     0x0001   声明为public,可以从它的包外访问

ACC_FINAL      0x0010   声明为final,不允许有子类

ACC_SUPER      0x0020   用invokespecial指令处理超类的调用

ACC_INTERFACE  0x0200   表明是一个接口,而不是一个类

ACC_ABSTRACT   0x0400   声明为abstract,不能被实例化

需要说明的是,这是针对一个Java类型的访问标志符列表,有的标志符只有类可以使用,有的标志符只有接口才可以使用,详情请查阅《JVM Spec》(2nded)。

(6) this_class

接下来的两个字节为this_class项,其值为一个对常量池表项的索引,即它指向一个常量池表项,而且该常量池表项必须为CONSTANT_Class_info表的结构。该表有一个name_index项,该项将指向另一个常量池表项,该表项包含了该类或者接口的完全限定名称。

(7) super_class

紧接着this_class之后的两个字节是super_class项,该项必须是对常量池表项的一个有效索引或者值为0。如果super_class项的值为0,则该Class文件必须表示java.lang.Object类。如果super_class项的值不为0,则又分为两种情况,若该Class文件表示一个类,则super_class项必须是对常量池中该类的超类的CONSTANT_Class_info表项的索引,这个超类和它的任何超类都不能是一个final类;若该Class文件表示一个接口,则super_class项必须是对常量池中表示java.lang.Object类的一个CONSTANT_Class_info表项的索引。

(8) interfaces_count和interfaces[ ]

紧接着super_class项后面的两个字节是interfaces_count项,此项表示由该类直接实现或者由该接口所扩展的超接口的数量。

紧接着interfaces_count项后面的是interfaces列表项,它包含了由该类直接实现或者由该接口所扩展的超接口的常量池索引,共计interfaces_count个索引。interfaces列表中的常量池索引按照该类型在源代码中给定的从左到右的顺序排列。

(9) fields_count和fields[ ]

接下来的是fields_count项,该项的值给出了fields列表项中的field_info表结构的数量,即表示了该Java类型声明的类变量和实例变量的个数总和。

fields列表项包含了在该Java类型中声明的所有字段的完整描述。fields列表中的每个field_info表项都完整地表示了一个字段的信息,包括该字段的名称、描述符和修饰符等。这些信息有的放在field_info表中,如修饰符;有的则放在field_info表所指向的常量池中,如名字和描述符。同前面的分析,fields列表项也是一个变长结构。   

需要说明的是,只有在该Java类型中声明的字段才可能在fields列表中列出,fields列表中不包括从超类或者超接口中继承而来的字段信息。

(10) methods_count和methods[ ]

在Class文件中,紧接着fields后面的是对在该Java类型中所声明的方法的描述。首先是methods_count项,它占两个字节长度,它的值表示对该Java类型中声明的所有方法的总计数。methods_count项后面是methods列表项,它由methods_count个连续的method_info表构成。每个method_info表都包含了与一个方法相关的信息,如方法名、描述符(即方法的返回值及参数类型)以及一些其它信息。如果一个方法既非abstract也非native,那么该method_info表将包含该方法局部变量所需的栈空间长度、为方法所捕获的异常表、字节码序列以及可选的行号表和局部变量表等信息。

需要说明的是,只有在该Java类型中显式定义的方法才可能在fields列表中列出,fields列表中不包括从超类或者超接口中继承而来的方法信息。

(11) attributes_count和attributes[ ]

Class文件中最后的部分是属性(attribute),它给出了在该Java类型中所定义的属性的基本信息。首先是attributes_count项,它占两个字节长度,它的值表示在后续的attributes列表中的attributes_info表的总个数。每个attributes_info表的第一项都是对常量池中CONSTANT_Utf8_info表项的一个索引,该表给出了此属性的名称。

需要说明的是,属性有很多种,在Class文件中的很多地方都出现了属性这一项,在顶层ClassFile表中有attributes属性项,在field_info表中也有attributes属性项,在method_info中也有attributes属性项,但是它们各有各的功能,详见上述分析。在《JVM Spec》(2nded)中,为ClassFile表结构的attributes列表项定义的唯一属性是SourceFile属性,为field_info表结构的attributes列表项定义的唯一属性是ConstantValue属性,为method_info表结构的attributes列表项定义的属性是Code属性和Exceptions属性。

总而言之,Class文件格式是一个规范性的格式。这个规范指的就是,上面提到的这些表结构本身的规范性,以及这些表结构之间的包含关系的规范性。实际上,《JVM Spec》(2nded)中就是通过表和项这两个概念来组织Class文件的格式的。首先,ClassFile表就是Class文件最外层的结构,换言之,这就是Class文件的格式。其次,ClassFile表又是一些项组成的,这些项的内容都要符合《JVM Spec》(2nded)中定义的规范,具体来说,若这个项的类型是基本类型,该项的值要符合规范,例如magic项一定要是0xCAFEBABE,access_flags项的值一定要是有效的标志值等等;若这个项的类型是一个表名,即该项是一个数组项,那么该数组项列表中的每一个表项都要是一个合法的、规范的表,不能是一个规范中没有定义的新表,这就是包含关系的规范性,同样,列表项中的每个表项本身也都要是符合其规范定义的表项,例如常量池列表中的某个CONSTANT_Class_info表的name_index项不是对一个CONSTANT_Utf8_info表结构的索引,那么这个常量池的表项就不是一个合法的表项,因而这个常量池列表项就是不符合规范的,因而整个文件就是不符合规范的。

分享到:
评论

相关推荐

    修改jar包中的class文件

    在Java开发过程中,有时我们需要对已经打包好的JAR文件中的class文件进行修改,这通常是由于修复bug、更新功能或者优化代码等原因。本文将详细介绍如何在不重新编译整个项目的情况下,修改并替换JAR文件中的class...

    java class文件反编译

    2、可处理多个*.class文件,可以处理文件夹内的所有文件,甚至可以处理*.jar文件; 3、带有多页面文本编辑器,也可集成在资源管理器中,随时点击右键都可进行操作; 4、支持java语法的高亮显示; 使用说明: ======...

    class文件编译器.zip

    这个压缩包`class文件编译器.zip`很可能包含了关于如何将Java源代码编译成`.class`文件的相关工具和信息。`ReadMe.txt`可能是提供编译过程的说明或者编译器使用的指南。 1. **Java源代码与Class文件**: Java源...

    在Eclipse中反编译Class文件完全详解

    在Java开发过程中,有时我们需要查看或理解某个JAR包中未提供源代码的Class文件的内部实现。本文详细介绍了如何在Eclipse环境中反编译Class文件,以便查看对应的源码。 首先,反编译Class文件是为了能理解那些无法...

    Beyond BCompare4 解压可用 可解析class文件

    Beyond BCompare4 解压可用 可解析class文件 Beyond Compare 是一款强大专业的文件和文件夹对比工具。使用它可以很方便地比较出两个文件或文件夹的差异,相差的每一个字节用颜色加以标识,让您查看方便,支持众多种...

    java反编译工具(.class文件反编译成.java文件)

    Java反编译是将已编译的字节码(.class文件)转换回源代码(.java文件)的过程,这对于理解和学习已有的Java程序、逆向工程或调试都是很有用的。标题提到的"java反编译工具"是用于这个目的的软件,它能够帮助开发者...

    class文件反编译工具

    “class文件反编译工具”是一种实用的开发辅助工具,它允许开发者查看和理解已编译的Java程序内部结构,这对于学习开源库、调试、逆向工程或者分析恶意软件的行为非常有帮助。其中,`jd-gui`是一个常见的开源Java反...

    class文件反编译

    在Java编程语言中,"class文件反编译"是一个关键概念,它涉及到程序的可执行代码与源代码之间的转换。Java程序首先由开发者编写成.java源文件,然后通过Java编译器(javac)编译成.class文件,这是Java虚拟机(JVM)...

    Class文件反编译工具

    在某些情况下,我们可能需要查看或理解这些二进制文件的原始Java代码,这时就需要用到"Class文件反编译工具"。 "Class文件反编译工具"是一种专门用来将`.class`和`.jar`文件转换回可读的Java源代码格式的软件。这种...

    Java反编译工具把.class文件转换为.java文件

    ### Java反编译工具:将.class文件转换为.java文件 #### 概述 在软件开发领域,有时我们可能需要分析第三方库或者开源项目的内部结构,这时就需要借助于反编译工具来帮助我们阅读和理解其源码。Java作为一种广泛...

    ClassFinal-java class文件安全加密工具

    在Java应用开发中,源代码的保护至关重要,因为Java的字节码(class文件)是可被反编译的,这可能导致源代码泄露、恶意篡改等安全风险。ClassFinal通过特定的加密算法和混淆技术,对Java类文件进行处理,使得未经...

    快速导出class文件插件 集合svn

    标题中的“快速导出class文件插件 集合svn”指的是一个专门针对开发者设计的工具,它结合了快速导出Java编译后的class文件功能和版本控制系统Subversion(SVN)的集成。这个插件旨在提高开发者的效率,允许他们便捷...

    Java Class文件反编译工具 jd-gui

    Java Class文件是Java程序编译后的二进制格式,它包含了类和接口的定义、方法体、常量池等信息,但这些信息是以机器可读的字节码形式存在,对于人类来说不易理解。为了查看和理解Class文件内部的源代码,我们就需要...

    class文件结构浅析

    ### Class文件结构浅析 #### 一、引言 在深入了解Class文件的结构之前,我们需要明确Class文件在Java生态系统中的重要地位。Java程序被编译器编译后生成的`.class`文件,实际上是字节码文件,它们是Java虚拟机...

    java class文件查看工具

    Java Class文件是Java源代码经过编译器处理后的二进制形式,它们包含了程序的结构信息,如类、接口、变量、方法等定义。在Java开发过程中,有时我们需要查看或分析这些Class文件,以理解程序运行原理、调试或逆向...

    class文件查看器.rar

    《深入解析Java Class文件查看器》 在Java编程领域,理解class文件的内部结构对于开发者来说至关重要,因为它是Java程序运行的基础。今天我们将探讨一款名为"class文件查看器"的工具,它是一个跨平台的实用程序,专...

    JavaDecompiler打开class文件

    它允许用户查看并理解已编译的.class文件的内容,因为Java的源代码(.java文件)通常不会随可执行文件一起发布。`JavaDecompiler`这个标题暗示我们将探讨如何使用Java反编译器来打开和解析.class文件。 Java类文件是...

    么把.class文件反编译成.java 经修改后再编译成.class

    下面将详细介绍.class 文件反编译到.java 文件的过程,包括反编译工具的使用和反编译后的修改、再编译等步骤。 一、反编译工具的选择 在反编译.class 文件时,需要使用专门的反编译工具。目前有多种反编译工具可供...

    java class文件编译

    Java Class文件是Java程序经过编译后的二进制表示形式,它是Java虚拟机(JVM)执行的基础。本文将深入探讨Java类文件的结构、编译过程以及如何使用提供的工具进行查看。 一、Java类文件结构 Java源代码(.java文件...

    输出JDK和CGLib动态代理产生的class文件.zip

    - target/classes:编译后的class文件,包括目标接口和实现类的class文件,以及由Proxy生成的代理类class文件 - 测试代码:展示如何使用Proxy创建代理对象并调用方法 2. CGLib代理项目: - src/main/java:包含...

Global site tag (gtag.js) - Google Analytics