(一)深入理解Java字节码文件
1.java虚拟机
java之所以能够实现“一次变异,处处运行”,功不可没的首先当属字节码(.class文件)。与c/c++不同的是,java源代码的默认编辑结果并非是可执行代码(本地机器指令),而是具有平台通用型的字节码。尽管不同平台虚拟机的内部实现机制不尽相同,但是他们能共同解释出的字节码却是一样的,所以说字节码才是跨平台的关键因素。
JVM是有一组规范定义出的抽象计算机,**JVM的主要任务是将字节码装载到其内部,解释/编译为对应平台上的机器指令执行。**java源码必须被编译成符合JVM规范的字节码,这是java前端编译器的工作。java虚拟机叫做java.exe,而java前段编译器则叫做javac.exe。
2.字节码文件的内部组成结构
1)magic(魔术)
一个有效的字节码文件的前四个字节为0xCAFEBABE,也被称为魔术。魔术是JVM用来校验所读取的目标文件是否是一个有效且合法的字节码文件。
2)minor_version(次版本号)和major_version(主版本号)
紧跟在magic之后的四个字节就是编译的次版本号和主版本号,它们共同构成了字节码文件的版本号,通常只有给定主版本号和一系列的次版本号之后,JVM才能读取一个字节码文件。
3)constant_pool_count(常量池计数器)和constant_pool(常量池)
在字节码文件中,紧跟在 次版本号和主版本号后面的就是常量池计数器和常量池。常量池中主要用于存放字面量和符号引用两大类数据常量,其访问形式是通过索引来访问的,因为常量池中的常量数并不固定,因此在常量池之前需要通过一个两个字节的常量池计数器来统计常量池列表中有多少个常量项
常量池中存放的字面量由文字字符串,final常量值等构成,而符号引用则包括了类和接口的全限定名,字段的名称和描述符,以及方法的名称和描述符。
4)acess_flag(访问标志)
紧跟在常量池之后的两个字节就是访问标志,访问标志主要用于表示某个类或者接口的访问权限。比如:访问标志指明的字节码文件中的类还是接口,使用的访问修饰符是哪一种。
5)this_class(类索引)和super_class(超类索引)
紧跟在访问标志之后的四个字节就是类索引和超类索引,类索引和超类索引各自汇通过索引指向常量池列表中一个类型为CONSTANT_Class_info的常量项。简单来说,类索引用于确定当前类的全限定名,超类索引用于确实能够当前的超类全限定名(超类索引只能有一个)
6)interface_count(j接口计数器)和interfaces(接口表)
接口计数器用于表示当前类或者当前接口的直接超类接口数量,接口表实际上是一个数组集合,它包含了当前类或者接口在常量池列表中直接超类接口的索引集合,通过这个索引即可确定当前类或者接口的超类接口的全限定名
7)field_count(字段计数器)和fields(字段表)
字段计数器用于表示一个类中类变量和实例变量的数量总和。而字段表实际上是一个数组集合,字段表中的每一个成员都必须是一个field_info结构的数据项,简单来说filed_info是用来表示一个字段的完整信息,比如字段的标识符,访问修饰符(public/private/protected),是类变量还是实例变量(static修饰符),是否是常量(final修饰符)等。
8)methods_count(方法计数器)和method(方法表)
方法计数器用于表示一个类中method_info表总数。而方法表实际上是一个数组集合,字段表中的每一个成员都必须是一个method_info结构的数据项,简单来说method_info是用来表示当前类或者接口中某个方法的完整描述,比如方法的标识符,方法访问修饰符(public/private/protected),方法的返回值类型和方法的参数信息等。
9)attributes_count(属性计数器)和attributes(属性表)
属性计数器用于表示当前字节码文件中attribute_info表总数。而属性表实际上是一个数组集合,属性表中的每一个成员都必须是一个attribute_info结构的数据项,每一个attribute_info表的第一项都是指向常量池列表中CONSTANT_Utf8_info项的索引,该表给出了属性的名称