前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java类文件的结构

Java类文件的结构

作者头像
爱撸猫的杰
发布2019-03-28 14:45:00
6440
发布2019-03-28 14:45:00
举报

 Class文件是以8位字节为基础单位的二进制流,各部分中间没有分隔符。遇到8位字节以上的空间数据项时,则会按照高位在前的方式分割成若干个8位字节进行存储。

    Class文件采用类似C语言的伪结构体来存储,这种伪结构体只有两种数据类型:无符号数和表。无符号数以u1,u2,u4,u8四种,数字代表字节数。可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值。表是由多个无符号数或其他表作为数据项构成的复合数据类型,所有表习惯以“info”结尾。表用于描述有层次关系的复合结构数据,整个Class文件本质上就是一张表。

总览如下:

魔数与Class文件的版本

1.魔数0XCAFEBABE 2.次版本号和主版本号

常量池

1.类和接口的全限定名 2.字段的名称和描述符 3.方法的名称和描述符

访问标志

1.类的访问信息 2.接口的访问信息

类索引、父类索引 和接口索引集合

存储类、父类、接口的 文件索引

字段表集合

1.字段作用域 2.是否static 3.可变性 4.并发可见性 5.可否被序列化 6.字段数据类型 7.字段名称

方法表集合

1.访问标志 2.名称索引 3.描述符索引 4.属性表集合

属性表集合

1.Code属性 2.Exceptions属性 3.LocalVariableTable属性 4.LineNumberTable属性 5.SourceFile属性 6.ConstantValue属性 7.InnerClasses属性 8.Deprecated和Synthetic属性 9.StackMapTable属性 10.Signature属性 11.BootstrapMethods属性

1.魔术与Class文件版本

    每个Class文件头四个字节称为魔数(Magic Number),作用是确定这个文件是不是一个Class文件,其值为0xCAFEBABE。紧跟着其后的4个字节存储的是Class文件的版本号:第5个和第6个字节是次版本号,第7个和第8个字节是主版本号。

2.常量池

    紧接着主版本号之后的是常量池入口,入口处放置一项u2类型的数据,代表常量池容量计数值(constant_pool_count),计数器从1开始,0是为了满足后面某些值项常量池索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”。

    常量池之中主要存放两大类常量:字面量(Literal) 和 符号引用(Symbolic References) 。字面量比较接近于Java语言层面的常量概念。而符号引用则属于编译原理方面的概念,包括了下面三类常量:

  1. 类和接口的全限定名(Fully Qualified Name)
  2. 字段的名称和描述符(Descriptor)
  3. 方法的名称和描述符

    常量池中的每一项常量都是一个表,共有14总结构各不相同的表结构数据,这11种表都有一个共同的特点,就是表开始第一位是一个u1类型的标志位(tag,取值为1置12,缺少标志为2的数据类型),14种常量具体含义如下:

类型

标志

描述

CONSTANT_Utf8_info

1

UTF-8编码的字符串

CONSTANT_Integer_info

3

整型字面量

CONSTANT_Float_info

4

浮点型字面量

CONSTANT_Long_info

5

长整型字面量

CONSTANT_Double_info

6

长整型字面量

CONSTANT_Class_info

7

类或接口的符号引用

CONSTANT_String_info

8

字符串类型字面量

CONSTANT_Fieldref_info

9

字段符号引用

CONSTANT_Methodref_info

10

类中方法的符号引用

CONSTANT_InterfaceMethodref_info

11

接口中方法的符号引用

CONSTANT_NameAndType_info

12

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

CONSTANT_MethodHandle_info

15

表示方法句柄

CONSTANT_MethodType_info

16

标识方法类型

CONSTANT_InvokeDynamic_info

18

标识一个动态方法调用点

3.访问标志

  常量池结束之后,紧接着的两个字节代表访问标志(access_flags),这个标志用于识别一些类或者接口层次的访问信息。具体标志为以及标志的含义如下:

标志名称

标志值

含义

ACC_PUBLIC

0x0001

是否为public类型

ACC_FINAL

0x0010

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

ACC_SUPER

0x0020

是否允许使用invokespecial字节码指令的新语意,invokespecial指令的语意在JDK 1.02发生过改变,为了区别这条指令使用哪种语意,JDK 1.02之后编译出来的类这个标志都必须为真

ACC_INTERFACE

0x0200

标识这是一个接口

ACC_SYNTHETIC

0x1000

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

ACC_ABSTRACT

0x0400

是否为abstract类型,对于接口或者抽象类来说,此标志值为真,其他类值为假

ACC_ANNOTATION

0x2000

标识这是一个注解

ACC_ENUM

0x4000

标识这是一个枚举

access_flags 中一共有16个标志位可以使用,当前之定义了其中的8个,没用使用到的标志位要求一律为0。

4.类索引、父类索引与接口索引集合

类索引(this_class)和父类索引(super_class)都是一个u2类型的数据,而接口索引集合(interface)是一组u2类型的数据集合,Class文件中由这三项数据来确定这个类的继承关系。类索引用于确定这个类的全限定名,父类索引用于确定这个类的父类的全限定名。因为Java不支持多重继承,所以父类索引只有一个,除了java.lang.Object外,所有Java类都有父类,因此除了java.lang.Object,所有java类的父类索引都不为0。接口索引集合就用来描述这个类实现了哪些接口,顺序为implements后面从左到右排列在接口索引集合中。

    类索引、父类索引和接口索引都按顺序排列在访问标志之后,类索引和父类索引引用两个u2索引值表示,它们各自指向一个类型为CONSTANT_Class_info的类描述符常量,通过CONSTANT_Class_info类型常量中的索引值可以找到定义在CONSTANT_Utf8_info类型的常量中的全限定名字字符串。

    对于接口索引集合,入口第一项——u2类型的数据为接口计数器(interfaces_count),表示索引表的容量。如果该类没有实现任何接口,则该计数器值为0,后面索引不在占用任何字节。

5.字段表集合

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

类型

名称

数量

u2

access_flags

1

u2

name_index

1

u2

descriptor_index

1

u2

attributes_count

1

attribute_info

attributes

attributes_count

access_flags是字段访问标志,标志如下:

标志名称

标志值

含义

ACC_PUBLIC

0x0001

字段是否public

ACC_PRIVATE

0x0002

字段是否private

ACC_PROTECTED

0x0004

字段是否protected

ACC_STATIC

0x0008

字段是否static

ACC_FINAL

0x0010

字段是否final

ACC_VOLATILE

0x0040

字段是否volatile

ACC_TRANSIENT

0x0080

字段是否transient

ACC_SYNTHETIC

0x1000

字段是否由编译器自动产生的

ACC_ENUM

0x4000

字段是否enum

    name_index 和 descriptor_index都是对常量池的引用,分别代表这字段的简单名称以及字段和方法的描述符。

    attribute_info用于存一些额外信息,如final static int m =123;

方法和字段的描述符作用是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。根描述规则,基本数据类型(byte、char、double、float、int、long、short、boolean)以及代表无返回值的void类型都用一个大写字符来表示,对象类型使用字符L加对象的全限定名来表示。

标识字符

含义

B

基本类型byte

C

基本类型char

D

基本类型double

F

基本类型float

I

基本类型

J

基本类型long

S

基本类型short

Z

基本类型boolean

V

特殊类型void

L

对象类型,如Ljava/lang/Object

比如方法 int indexOf(char[]source,int sourceOffset,int sourceCount,char[]target,int targetOffset,int targetCount,int fromIndex)描述符为”([CII[CIII)I”

6.方法表集合

方法的描述和字段的描述几乎采用了完全一致的方式。方法表一次包括了访问标志(access_flags)、名称索引(name_index)、描述符索引(descriptor_index)、属性表集合(attributes)几项。方法表如下:

标志名称

标志值

含义

ACC_PUBLIC

0x0001

方法是否为public

ACC_PRIVATE

0x0002

方法是否为private

ACC_PROTECTED

0x0004

方法是否为protected

ACC_STATIC

0x0008

方法是否为static

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

方法是否是由编译器自动产生的

ACC_FINAL

0x0010

方法是否为final

7.属性表集合

  在Class文件、字段表、方法表都可以携带自己的属性表集合,用于描述某些场景专有的信息。

  预定义的属性如下:

属性名称

使用位置

含义

Code

方法表

Java代码编译成的字节码指令

ConstantValue

字段表

final关键字定义的常量值

Deprecated

类、方法表、字段表

被声明为deprecated的方法和字段

Exceptions

方法表

方法抛出的异常

EnclosingMethod

类文件

仅当一个类为局部类或者匿名类时才能拥有这个属性,这个属性用于标识这个类所在的外围方法

InnerClasses

类文件

内部类列表

LineNumberTable

Code属性

Java源码的行号和字节码指令的对应关系

LocalVariableTable

Code属性

方法局部变量描述

StackMapTable

Code属性

JDK1.6新增,供新的类型检查验证器检查和处理目标方法的局部变量和操作数栈所需要的类型是否匹配

Signature

类、方法表、字段表

JDK1.5新增,用于支持泛型的情况下的方法签名

SourceFile

类文件

记录源文件名称

SourceDebugExtension

类文件

JDK1.6新增,用于存储额外的调试信息。比如JSP调试

Synthetic

类、方发表、字段表

表示方法或字段为编译器自动生成的

LocalVariableTypeTable

JDK1.5新增,使用特征签名代替描述符

RuntimeVisibleAnnotations

类、方法表、字段表

JDK1.5新增,为动态注解提供支持

RuntimeVisibleParameterAnnotations

方法表

JDK1.5新增,类似RuntimeVisibleAnnotations,但作用对象为方法参数

AnnotationDefault

方法表

JDK1.5新增,用于记录注解类元素的默认值

RuntimeInvisibleAnnotations

方法表

JDK1.5新增,作用和RuntimeVisibleAnnotations属性作用相反,用于指定哪些注解是运行时不可见的

RuntimeInvisibleParameterAnnotations

方法表

JDK1.5新增,类似RuntimeInVisibleAnnotations,但作用对象是方法参数

BootstrapMethods

类文件

JDK1.7新增,用于保存invokedynamic指令引用的引导方法限定符

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-12-19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.魔术与Class文件版本
  • 2.常量池
  • 3.访问标志
  • 4.类索引、父类索引与接口索引集合
  • 5.字段表集合
  • 6.方法表集合
  • 7.属性表集合
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档