第6章 类文件结构

来源:互联网 发布:海马玩mac下载不了 编辑:程序博客网 时间:2024/05/16 08:26

概述:

类文件就是二进制和源码的中介

其实java可以被编译成.class文件、JRubyGroovy等都可以编译成.class文件,然后都java虚拟机上运行

1、Class类文件的结构

采用是8位字节为基础单位的二进制流,没有空隙,按顺序,

Class 文件格式采用一种类似于C语言结构,分为无符号树和表

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

2、魔数与Class文件的版本

2.1 、头4个字节称为魔数(MagicNumber):是否能被虚拟机接受的class文件, Class魔数为 : 0xCAFEBABE

2.2、4个字节存储是Class文件版本号:第5和第6字节是次版本号(Minor Version) 第7和第8个字节是主版本号(Major Version)

java版本从45开始 JDK 1.1 支持版本号为 45.0~45.65535  JDK1.7 的主版本号为51.0

写一个java编译成class文件,用winhex打开

public class ShowTime {private int m;public int inc(){return m + 1;}}

字节码


2.3、常量池

紧接版本之后就是常量池入口

第一个为u2类型的数据,代表常量池容量计数值(constant_pool_count) 从1 开始的

0x0013 十进制为19,代表18个常量 索引从1~18

常量池放入两大类常量:字面量和符号引用

字面量:如文本字符串、被声明为final的常量值等,

符号引用: 类和接口的权限定名、字段的名称和描述符、方法的名称和描述符

常量池中的每一项常量都是一个表,共有11中结构各不相同的表结构数据,第一个是一个u1类型的标志位(tag, 取值为1至12,缺少标志为2的数据类型)代表是这个常量是那种常量类型

类型标志描述CONSTANT_Utf8_info1UTF-8编码的字符串CONSTANT_Integer_info3整型字面量CONSTANT_Float_info4浮点型字面量CONSTANT_Long_info5长整型字面量CONSTANT_Double_info6双精度浮点型字面量CONSTANT_Class_info7类或接口的符号引用CONSTANT_String_info8字符串类型字面量CONSTANT_Fieldref_info9字段的符号引用CONSTANT_Methodref_info10类中方法的符号引用CONSTANT_InterfaceMethodref_info11接口中方法的符号引用CONSTANT_NameAndType_info12字段或方法的部分符号引用






在列为A表示 0x0A 代表十进制也就是CONSTANT_Methodref_info, 可以看到它将引用CONSTANT_Class_info,第四个常量 和 CONSTANT_NameAndType第15常量

第二个常量 0x09 ,即十进制9, 也就是CONSTANT_Fieldref_info, 可以它引用第3个CONSTANT_Class_info和第16(0x10)个CONSTANT_NameAndType

其余可以通过jdk自带工具算出:javap -verbose

D:\data\spittr>javap -verbose TestClassClassfile /D:/data/spittr/TestClass.class  Last modified 2017-7-26; size 275 bytes  MD5 checksum 2549ac0e12d6f173f679851de1545d98  Compiled from "TestClass.java"public class TestClass  minor version: 0  major version: 52  flags: ACC_PUBLIC, ACC_SUPERConstant pool:   #1 = Methodref          #4.#15         // java/lang/Object."<init>":()V   #2 = Fieldref           #3.#16         // TestClass.m:I   #3 = Class              #17            // TestClass   #4 = Class              #18            // java/lang/Object   #5 = Utf8               m   #6 = Utf8               I   #7 = Utf8               <init>   #8 = Utf8               ()V   #9 = Utf8               Code  #10 = Utf8               LineNumberTable  #11 = Utf8               inc  #12 = Utf8               ()I  #13 = Utf8               SourceFile  #14 = Utf8               TestClass.java  #15 = NameAndType        #7:#8          // "<init>":()V  #16 = NameAndType        #5:#6          // m:I  #17 = Utf8               TestClass  #18 = Utf8               java/lang/Object{  public TestClass();    descriptor: ()V    flags: ACC_PUBLIC    Code:      stack=1, locals=1, args_size=1         0: aload_0

可以看出,咱们推算是正确的。

2.4、访问标志

常量池结束紧接着的2个字节代表访问标志(access_flags

标志名称标志值含义ACC_PUBLIC0x0001是否为public类型ACC_FINAL0x0010是否被声明为final,只有类可设置ACC_SUPER0x0020是否允许使用invokespecial字节码指令,JDK1.2之后编译出来的类的这个标志为trueACC_INTERFACE0x0200标志这个是一个接口ACC_ABSTRACT0x0400是否为abstract类型,对于接口或抽象类来说,此标志值为true,其他值为falseACC_SYNTHETIC0x1000标志这个类并非由用户产生的ACC_ANNOTATION0x2000标识这个一个注解ACC_ENUM0x4000标识这是一个枚举

根据上面编译可以找到access_flags对应位置

access_flags中一共有32个标志为可以使用,当前只定义了其中的8个,可以进行或运算进行组合

TestClass这个类被public关键之修饰但没有被声明为final和abstract,ACC_PUBLICACC_SUPER标志为true,其它为false

access_flags的值: 0x0001|0x0020 = 0x0021


2.5 类索引、父类索引与接口索引集合

类索引(this_class)和父类索引(super_class)都是一个u2类型的数据,而接口索引集合(interface)是一组u2类型的数据的集合,Class文件中由这三个数据来确定这个类继承关系,类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类全限定名,由于java语言不允许多重继承,所以父类索引只有一个,除了java.lang.Object之外,所有的java类都有父类,因此除了java.lang.Object外,所有java类的父类索引都不为0.

2.6、字段表集合

字段包括了类级变量或实例级变量,但是不包括在方法内部声明的变量。

包含的信息:字段的作用域(public privateprotected)类级别还是实例级变量(static)可变性(final)、并发可见性(volatile,是否强制从主内存读取)、可否序列化(transient)、字段数据类型(基本类型、对象、数组)、字段名称

字段表结构

类型名称数量u2access_flags1u2name_index1u2descriptor_index1u2attributes_count1attribute_infoattributesattributes_count

字段访问标志

字段访问标志标志名称标志值含义ACC_PUBLIC0x0001字段是否publicACC_PRIVATE0x0002字段是否privateACC_PROTECTED0x0004字段是否protectedACC_STATIC0x0008字段是否staticACC_FINAL0x0010字段是否为finalACC_VOLATILE0x0040字段是否volatileACC_TRANSIENT0x0080字段是否transientACC_SYNTHETIC0x1000字段是否由编译器自动产生的ACC_ENUM0x4000字段是否enum

ACC_PUBLICACC_PRIVATEACC_PROTECTED不能同时选择

ACC_FINAL ACC_VOLATILE 不能同时选择

接口字段必须有ACC_PUBLICACC_STATICACC_FINAL

name_index:简单名称

descriptor_index:方法的描述符

简单名称:没有类型和参数修饰的方法或字段名称 ,这个类中的inc()方法和字段m 的简单名称分别为inc 和m

描述字段:用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值

标志字符含义B基本类型byteC基本类型charD基本类型doubleF基本类型floatI基本类型intJ基本类型longS基本类型shortZ基本类型booleanV特殊类型voidL对象类型 如 Ljava/lang/Object

java.lang.String[][] 二维数组 被记录为"[[Ljava/lang/String;"    int[]  被记录为“[I”

用于描述符来描述方法时,按照先参数列表,后返回值得顺序描述,参数列表按照参数严格顺序放在一组小括号之内。例如void inc() 描述符为 “()V”

方法java.lang.String.toString()的描述符为 "()Ljava/lang/String", 方法 int indexOf(char[] source,int sourceOffset, int sourceCount,char[] target, int targetOffset,int targetCount, int fromIndex) 的描述符为“([CII[CIII)I”

2.7 方法表集合

方法表的结构与字段表结构一样

字段访问标志标志名称标志值含义ACC_PUBLIC0x0001方法是否publicACC_PRIVATE0x0002方法是否privateACC_PROTECTED0x0004方法是否protectedACC_STATIC0x0008方法是否staticACC_FINAL0x0010方法是否为finalACC_SYNCHRONIZED0x0040方法是否sychronizedACC_BRIDGE0x0080方法是否是由编译器产生的桥接方法ACC_VARARGS0x1000方法是否接受不定参数ACC_NATIVE0x4000方法是否为nativeACC_ABSTRACT0x0080方法是否为AbstractACC_STRICT0x1000方法是否为strictfpACC_SYNTHETIC0x4000方法是否有编译器自动产生

方法中代码存在于code 中

2.8属性表集合

不严格按照顺序了,只要不重名就可以了

虚拟机规范预定的属性属性名称使用位置含义Code方法表java代码编译成的字节码指令ConstantValue字段表final关键字定义的常量值Deprecated类、方法表、字段表被表明为deprecated的方法和字段Exceptions方法表方法抛出的异常InnerClasses类文件内部类列表LineNumberTableCode 属性Java源码的行号与字节码指定的对应关系LocalVariableTableCode属性方法的局部变量描述SourceFile类文件源文件名称Synthetic类、方法表、字段表标识方法或字段为编译器自动生成的

属性表结构类型名称数量u2attribute_name_index1u2attribute_length1u1infoattribute_length

2.8.1 Code属性

Code属性表的结构类型名称数量u2attribute_name_index1u4attribute_length1u2max_stack1u2max_locals1u4code_length1u1codecode_lengthu2exception_table_length1exception_infoexception_tableexception_table_lengthu2attributes_count1attribute_infoattributesattributes_count

attribute_name_index是一项指向CONSTANT_Utf8_info类型常量的引用,常量值固定为“Code”,它代表了该属性的名称

attribute_length指示的属性值的长度,由于属性名称索引和属性长度一共占6个字节,所有最后减去6个字节

max_stack代表了操作数栈深度的最大值。

max_locals代表了局部变量表所需的存储空间。单位是Slot 32位用一个Slot 64位用两个Slot

code_length 和code用来存储java源程序编译后生成的字节码指令

code_length 虽然是u4类型的长度值 理论上达到2的32次方-1 ,实际上限制一个方法不允许超过65535条,也就是方法太长,java虚拟机拒绝编译

异常表

属性表结构类型名称数量u2start_pc1u2end_pc1u2handler_pc1u2catch_type1JVM虚拟机基于栈模型

2.8.2 Exceptions属性

Exceptions属性的作用是列出方法可能抛出的检查异常(Checked Exceptions)也就是方法描述时在throws关键字后面列举的异常。

属性表结构类型名称数量u2attribute_name_index1u4attribute_length1u2number_of_exceptions1u2exception_index_tablenumber_of_exception

2.8.3、LineNumberTable属性

描述java源码行号与字节码行号(字节码的偏移量)之间的对应的关系,它并不是运行时必须的属性,但是默认会生成Class文件之中,可以在javac带参数-g:none或-g:lines选项来取消或要求生成这项信息,如果不生成,抛出异常不会指定行号,也就不可以设置断点进行调试

LineNumberTable属性结构类型名称数量u2attribute_name_index1u4attribute_length1u2line_number_table_length1line_number_infoline_number_tableline_number_table_length

2.8.4 、LocalVariableTable属性

描述栈帧中局部变量表中的变量与java源码中定义变量之间的关系

默认也不会生成Class文件之中,可以通过-g:none-g:vars 设置 ,调试的时候的参数变量没有值

LocalVariableTable属性结构类型名称数量u2attribute_name_index1u4attribute_length1u2local_variable_table_length1local_variable_infolocal_variable_tablelocal_variable_table_length

local_variable_info项目结构类型名称数量u2start_pc1u2length1u2name_index1u2descriptor_index1u2index1

2.8.5/ SourceFile属性

用于记录生成这个Class文件的源码文件名称,这个属性可选的 通过 -g:none-g:source关闭或要求生成这项信息。内部类例外

SourceFile属性结构类型名称数量u2attribute_name_index1u4attribute_length1u2sourcefile_index1


2.8.6、ConstantValue属性

用于通知虚拟机自动为静态变量赋值。

ConstantValue属性结构类型名称数量u2attribute_name_index1u4attribute_length1u2constantvalue_index1

2.8.7 innerClasses属性

记录内部类与宿主类之前的关联

InnerClasses属性结构类型名称数量u2attribute_name_index1u4attribute_length1u2number_of_classes1inner_classes_infoinner_classesnumber_of_classes

inner_classes_info表结构类型名称数量u2inner_class_info_index1u2outer_class_info_index1u2inner_name_index1u2inner_class_access_flags1

inner_class_access_flags是内部类的访问标志

inner_class_access_flags标志标志名称标志值含义ACC_PUBLIC0x0001内部类是否为publicACC_PRIVATE0x0002内部类是否为privateACC_PROTECTED0x0004内部类是否为protectedACC_STATIC0x0008内部类是否为staticACC_FINAL0x0010内部类是否为finalACC_SYNCHRONIZED0x0020内部类是否为synchronizedACC_ABSTRACT0x0400内部类是否为abstractACC_SYNTHETIC0x1000内部类是否并非用户代码产生的ACC_ANNOTATION0x2000内部类是否一个注解ACC_ENUM0x4000内部类是否是一个枚举

总算勉强写完了。。。。。。

原创粉丝点击