Class类文件的结构

5 篇文章 0 订阅

        JAVA实现平台无关性的基础是虚拟机和字节码存储格式,使用Java编译器可以把Java代码编译为存储字节码的Class文件,使用JRuby等其他语言的编译器一样可以把程序代码编译成Class文件,虚拟机并不关心Class的来源是什么语言,只要它符合Class文件应有的结构就可以在Java虚拟机中运行。

        Java语言中的各种变量、关键字和运算符号的语义最终都是由多条字节码命令组合而成的,因此字节码命令所能提供的语义描述能力肯定比Java语言本身更强大。Class文件是一组以8位字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符,这使得整个Class文件存储的内容几乎全部都是程序运行的必要数据,没有空隙。当遇到需要占用8位字节以上空间的数据项时,则会按照高位在前的方式分割成若干个8位字节进行存储。

        根据Java虚拟机规范的规定,Class文件格式采用一种类似于C语言结构体的伪结构表示。与C语言结构体的域不同,连续的项Java Class文件中顺序存储,不进行填充或者对齐。在《JVM Specification》中式这样定义Class文件的结构:

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];
   }

        接下来我们一起看看这个表中各个数据项的具体含义。使用下面的测试类进行详细说明:

package com.igood;

public class TestClass {
	private int member;//int类型字段
	public static String HOME_URL="http://www.baidu.com";//static字符字段
	public final float PI = 3.14159F;//final字段
	//构造函数
	public TestClass(){
		
	}
	//getMember函数
	public int getMember() {
		return member;
	}	
}
        编译完成的类文件如下:

1、 magic

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


2、minor_version和major_version
         紧接着魔数的4个字节存储的是Class文件的版本号:第5、6个字节是次版本号(minor_version),第7、8字节是主版本号(major_version)。Java的版本号从45开始。


3、常量池

        紧接着主次版本号之后的是常量池入口,由于常量池中常量的数量是不固定的,所以再常量池的入口需要放置一项u2的类型的数据,代表常量池容量计数值(constant_pool_count),这个容量计数是从1而不是从0开始的。常量池(constant_pool)存储了诸如符号常量、final常量值、基本数据类型的字面值等内容。在jVM的头文件jvm/vmcommon/h/pool.h中,有以下对常量池项类型的定义:

#define CONSTANT_Utf8                       1 //UTF-8编码的Unicode字符串
#define CONSTANT_Integer                    3 //int类型的字面值
#define CONSTANT_Float                      4 //float类型的字面值
#define CONSTANT_Long                       5 //long类型的字面值
#define CONSTANT_Double                     6 //double类型的字面值
#define CONSTANT_Class                      7 //对一个类或接口的符号引用
#define CONSTANT_String                     8 //String类型字面值的引用
#define CONSTANT_Fieldref                   9 //对一个字段的符号引用
#define CONSTANT_Methodref                  10 //对一个类中方法的符号引用
#define CONSTANT_InterfaceMethodref         11 //对一个接口中方法的符号引用
#define CONSTANT_NameAndType                12 //对一个字段或方法的部分符号引用
constant_pool:表类型数据集合,即常量池中每一项常量都是一个表,共有11种结构各不相同的表结构数据。这11种表都有一个共同的特点,即均由一个u1类型的标志位开始,可以通过这个标志位来判断这个常量属于哪种常量类型,常量类型及其数据结构如下表所示:

类型

简介

项目

类型

描述

CONSTANT_Utf8_info

utf-8缩略编码字符串

tag

u1

值为1

length

u2

utf-8缩略编码字符串占用字节数

bytes

u1

长度为length的utf-8缩略编码字符串

CONSTANT_Integer_info

整形字面量

tag

u1

值为3

bytes

u4

按照高位在前储存的int值

CONSTANT_Float_info

浮点型字面量

tag

u1

值为4

bytes

u4

按照高位在前储存的float值

CONSTANT_Long_info

长整型字面量

tag

u1

值为5

bytes

u8

按照高位在前储存的long值

CONSTANT_Double_info

双精度浮点型字面量

tag

u1

值为6

bytes

u8

按照高位在前储存的double值

CONSTANT_Class_info

类或接口的符号引用

tag

u1

值为7

index

u2

指向全限定名,常量项的索引

CONSTANT_String_info

字符串类型字面量

tag

u1

值为8

index

u2

指向字符串字面量的索引

CONSTANT_Fieldref_info

字段的符号引用

tag

u1

值为9

index

u2

指向声明字段的类或接口描述符CONSTANT_Class_info的索引项

index

u2

指向字段描述符CONSTANT_NameAndType_info的索引项

CONSTANT_Methodref_info

类中方法的符号引用

tag

u1

值为10

index

u2

指向声明方法的类描述符CONSTANT_Class_info的索引项

index

u2

指向名称及类型描述符CONSTANT_NameAndType_info的索引项

CONSTANT_InterfaceMethodref_info

接口中方法的符号引用

tag

u1

值为11

index

u2

指向声明方法的接口描述符CONSTANT_Class_info的索引项

index

u2

指向名称及类型描述符CONSTANT_NameAndType_info的索引项

CONSTANT_NameAndType_info

字段或方法的部分符号引用

tag

u1

值为12

index

u2

指向该字段或方法名称常量项的索引

index

u2

指向该字段或方法描述符常量项的索引


4、access_flags(访问标志) 占用2个字节。用来表明该class文件中定义的是类还是接口,访问修饰符是否定义为public;是否定义为abstract类型。类是否是final的。

标志名称

标志值

含义

ACC_PUBLIC

0x0001

是否为public类型

ACC_FINAL

0x0010

是否被声明为final,只有类可设置

ACC_SUPER

0x0020

是否允许使用invokespecial字节码指令,JDK1.2以后编译出来的类这个标志为真

ACC_INTERFACE

0x0200

标识这是一个接口

ACC_ABSTRACT

0x0400

是否为abstract类型,对于接口和抽象类,此标志为真,其它类为假

ACC_SYNTHETIC

0x1000

标识别这个类并非由用户代码产生

ACC_ANNOTATION

0x2000

标识这是一个注解

ACC_ENUM

0x4000

标识这是一个枚举




         根据上面的表格,TestClass类的访问标志为0x0021 = ACC_PUBLIC | ACC_SUPER = 0x0001 | 0x0020

5、this_class 占用2个字节。 它是一个对常量池的索引。指向的是常量池中存储类名符号引用的CONSTANT_Class_info常量表(见下面常量池具体结构)。比如this_class=0x0001。则表示指向常量池中的第一个常量表。通常这个表是指向当前class文件所定义的类名。


6、super_class 占用2个字节 与this_class类似,指向存放当前class文件所定义类的超类名字的索引的CONSTANT_Class_info常量表。


7、inteface_count、interfaces 

        interface_count是class文件所定义的类直接实现的接口或父类实现的接口的数量。占2个字节。intefaces包含了对每个接口的CONSTANT_Class_info常量表的索引。


由于interface_count为0,所以接口索引集合interfaces大小为0,即在编译后的二进制文件中不存在interfaces这项内容。

8、fields_count、fields

        fields_count字段计数器,表明了类中字段的数量 。fields是不同长度的field_info表的序列。这些field_info表中并不包含超类或父接口继承而来的字段。field_info表展示了一个字段的信息,包括字段的名字,描述符和修饰符。如果该字段是final的,那么还会展示其常量值。注意,这些信息有些存放在field_info里面,有些则存放在field_info所指向的常量池中。fields:字段表集合,一组字段表类型数据的集合,字段表包括access_flags,name_index,descriptor_index,attributes_count和attributes[attributes_count]


9、method_count、methods

        与字段类似,method_count表明类中方法的数量和每个方法的常量表的索引。methods表明了不同长度的method_info表的序列。methods:方法表集合,一组方法表类型数据的集合。方法表结构和字段表结构一样包括access_flags,name_index,descriptor_index,attributes_count和attributes[attributes_count]

标志名称

标志值

含义

ACC_PUBLIC

0x0001

字段是否为public

ACC_PRIVATE

0x0002

字段是否为private

ACC_PROTECTED

0x0004

字段是否为protected

ACC_STATIC

0x0008

字段是否为static

ACC_FINAL

0x0010

字段是否为final

ACC_SYNCHRONIZED

0x0020

字段是否为synchronized

ACC_BRIDGE

0x0040

方法是否是由编译器产生的桥接方法

ACC_VARARGS

0x0080

方法是否接受不定参数

ACC_NATIVE

0x0100

字段是否为native

ACC_ABSTRACT

0x0400

字段是否为abstract

ACC_STRICTFP

0x0800

字段是否为strictfp

ACC_SYNTHETIC

0x1000

字段是否为编译器自动产生





10、attributes_count 和 attributes属性表

         在Class文件、属性表、方法表中都可以包含自己的属性表集合,用于描述某些场景的专有信息与Class文件中其它数据项对长度、顺序、格式的严格要求不同,属性表集合不要求其中包含的属性表具有严格的顺序,并且只要属性的名称不与已有的属性名称重复,任何人实现的编译器可以向属性表中写入自己定义的属性信息。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值