类文件结构

一、无关性的基石
1.java虚拟机只与不同语言编译器编译后的特定的二进制文件格式的class文件进行关联,并且这些class文件要遵循java虚拟机规范中的许多强制性和结构化约束。所以它是跨平台和跨语言的。

二、class类文件结构
1.Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在Class文件之中,中间没有添加任何分隔符,没有空隙存在。当遇到需要占用8位字节以上空间的数据的数据项时,则会按照高位在前的方式分割成若干个8位字节进行存储。

2.Class文件格式采用一种类似于C语言结构体的伪结构来存储数据,分为两种数据类型:
无符号数:无符号数属于基本的数据类型,以u1,u2,u4,u8来分别代表1个字节,2个字节,4个字节,8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。
表:由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯以“-info”结尾。表用于描述有层次关系的复合结构的数据。
无论无符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的容量计数器加若干个连续的数据项的形式,这时称这一系列连续的某一类型的数据为某一类型的集合。

这里写图片描述

(1)magic
每个Class文件的头4个字节称为魔数,它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件,值为:0xCAFEBABE。

(2)minor_version和major_version
每个class文件的第5和第6个字节是次版本号,第7和第8个字节是主版本号。java的版本号从45开始,jdk1.1之后每个jdk大版本发布主版本号向上加1,高版本可以向下兼容,低版本不能运行高版本的class文件,不然会抛出java.lang.UnsupportedClassVersionError。

(3)constant_pool_count和constant_pool
常量池是class文件之中的资源仓库,它是class文件结构中与其他项目关联最多的数据类型,也是占用class文件空间最大的数据项目之一,同时他还是在class文件中第一个出现的表类型数据项目。

    constant_pool_count,代表常量池容量计数值,由于这个值是从1而不是0开始,所以常量池容量的值是constant_pool_count-1,而空出来的第0项表示“不引用任何一个常量池项目”。

    constant_pool主要存放两大类常量:
    字面量:字面量比较接近于java语言层面的常量概念,如文本字符串、声明为final的常量值。
    符号引用:包含三类常量。类和接口的全限定名;字段的名称和描述符;方法的名称和描述符。

    常量池中每一项常量都是一个表,总共有14种表,每种表开始的第一位是一个u1类型的标志位,代表当前这种常量属于哪种类型常量。

(4)access_flags
访问标志占两个字节,用于识别一些类或者接口层次的访问信息,包括:这个class是类还是接口;是否定义为public类型;是否为abstract类型;如果是类,是否被声明为final等。

(5)this_class,super_class和interfaces
类索引和父类索引都是u2类型数据,接口索引集合是一组u2类型数据的集合,class文件中由这三项数据来确定这个类的继承关系。类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名,由于java不允许多继承,所以一个类只有一个父类索引,Object类除外。接口索引集合用于描述这个类实现了哪些接口,因此使用interfaces_count来存储接口数量。

(6)field
字段表(field_info)用于描述接口或者类中声明的变量。字段包括类级变量以及实例级变量,但不包括在方法内部声明的局部变量。包括的信息有:字段的作用域(private、public、protected)、是实例变量还是类变量(static修饰符)、可变性(final)、并发可见性(volitile修饰符,是否强制从主内存写)、可否被序列化(transient修饰符)、字段数据类型(基本类型、对象、数组)、字段名称。

    类的全限定名,是把类的全名中的.替换成“/”;而简单名称是指没有类型和参数修饰的方法或者字段名称。

    用描述符描述方法时,按照先参数列表,后返回值的顺序描述,参数列表按照参数的严格顺序放在一组小括号()之内。
    字段表集合中不会列出从超类或者父类接口中继承而来的字段,但有可能列出java代码中不存在的字段,java 中字段是无法重载的,但在字节码中只要两个字段描述符不一致,那么两个字段重名是合法的。

(7)method
method_info方法表用于描述类或者接口中声明的方法,methods_count用于表示Class文件中方法总数,method方法存储了方法的访问标识、是否静态、是否final、是否同步synchronized、是否本地方法native、是否抽象方法abstract、方法返回值类型、方法名称、方法参数列表等信息。

方法的代码指令并没有直接存放在方法表中,而是存放着属性表中的方法表Code中。
注意:如果父类的方法在子类没有被重写,方法表中不会出现来自父类的方法信息,但是编译器会自动添加类构造器””方法和实例构造器””方法。

Java编译器的方法特征签名只包括:方法名称、参数顺序和参数类型,不包括方法返回值类型,因此java的方法重载不能通过方法的返回值类区别,但是在Class文件中,方法特征签名包括方法的返回值类型,因此Class文件中可以共存两个名称和参数完全相同而返回值类型不同的方法。

(8). attribute:
attribute_info属性表是Class文件格式中最具扩展性的一种数据项目,用于存放field_info字段表、method_info方法表以及Class文件的专有信息,属性表不要求各个属性有严格顺序,只要求不与已有的属性名字重复即可,属性表中存放的常用信息如下
这里写图片描述

三、Class类结构的示例
使用JDK提供的javap工具可以简单将Class反编译,编译理解Class文件的结构,例子如下:
源码:

public class Test {    
    public int getNum(int i) {    
        return i + 1;    
    }    
}

javap反编译之后的字节码文件:

public class Test extends java.lang.Object    
  SourceFile: "Test.java"    
  minor version: 0    
  major version: 50    
//常量池  
 Constant pool:    
const #1 = class        #2;       
const #2 = Asciz        Test;    
const #3 = class        #4;       
const #4 = Asciz        java/lang/Object;    
const #5 = Asciz        <init>;  //实例构造器  
const #6 = Asciz        ()V;  //void返回类型  
const #7 = Asciz        Code;  //属性表Code属性  
const #8 = Method       #3.#9;  //方法特征签名  java/lang/Object."<init>":()V    
const #9 = NameAndType  #5:#6;//  方法名称和返回值"<init>":()V    
const #10 = Asciz       LineNumberTable;  //属性表源码行号和字节码指令对应表  
const #11 = Asciz       LocalVariableTable;  //属性表方法局部变量表  
const #12 = Asciz       this;  //Test类实例对象本身  
const #13 = Asciz       LTest;;  //对象类型,Test类  
const #14 = Asciz       getNum;  //方法名称  
const #15 = Asciz       (I)I;  //方法参数列表为一个int类型和返回值为int类型  
const #16 = Asciz       i;  //参数名称i  
const #17 = Asciz       I;  //参数类型int  
const #18 = Asciz       SourceFile;    
const #19 = Asciz       Test.java;    
 //方法表  
{    
//构造函数(默认构造方法)  
public Test();    
  Code:  //属性表Code属性  
   Stack=1, Locals=1, Args_size=1    
   0:   aload_0    
   1:   invokespecial   #8; //Method java/lang/Object."<init>":()V    
   4:   return    
  LineNumberTable:    
   line 2: 0    

  LocalVariableTable:  //属性表方法局部变量表  
   Start  Length  Slot  Name   Signature    
   0      5      0    this       LTest;    

//自定义方法  
public int getNum(int);    
  Code:    
   Stack=2, Locals=2, Args_size=2    
   0:   iload_1    
   1:   iconst_1    
   2:   iadd    
   3:   ireturn    
  LineNumberTable:    
   line 4: 0    

  LocalVariableTable:    
   Start  Length  Slot  Name   Signature    
   0      4      0    this       LTest;    
   0      4      1    i       I    
}    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值