深入理解Java虚拟机读书笔记之:第6章 Java class文件

 Java class文件是对Java程序二进制文件格式的精确定义。每一个Java class文件都对一个Java类或者Java接口作出了全面描述。一个class文件中只能包含一个类或者接口。
    尽管class文件与Java语言结构相关,但它并不一定必须与Java语言相关。如下图,可以使用其他语言来编写程序,然后将其编译为class文件,或者把Java程序编译为另一种不同的二进制文件格式。


 
(注:如Scala、Groovy、JRuby等基于JVM的语言)
 
    Java class文件是8位字节的二进制流。数据项按顺序存储在class文件中,相邻的项之间没有任何间隔,这样可以使class文件紧凑。占据多个字节空间的项按照高位在前的顺序分为几个连续的字节存放。
    在class文件中,可变长度项的大小和长度位于其实际数据之前。这个特性使得class文件流可以从头到尾被顺序解析,首先读出项的大小,然后读出项的数据。
 
class文件的内容
    Java class文件中包含了Java虚拟机所需知道的、关于类或接口的所有信息。

class文件“基本类型”

类 型

描 述

u1

1个字节,无符号类型

u2

2个字节,无符号类型

u4

4个字节,无符号类型

u8

8个字节,无符号类型

 
    可变长度的ClassFile表中的项,按照它们在class文件中出现的顺序列出了主要部分。

ClassFile表的格式

类 型

名 称

数 量

u4

magic

1

u2

minor_version

1

u2

major_version

1

u2

constant_pool_count

1

cp_info

constant_pool

constant_pool_count-1

u2

access_flags

1

u2

this_class

1

u2

super_class

1

u2

interfaces_count

1

u2

interfaces

interfaces_count

u2

fields_count

1

field_info

fields

fields_count

u2

methods_count

1

method_info

methods

methods_count

u2

attributes_count

1

attribute_info

attributes

attributes_count

 
1)magic(魔数)
    每个Java class文件的前4个字节被称为它的魔数(magic number):0xCAFEBABE。魔数的作用在于,可以轻松地分辨出Java class文件和非Java class文件。
 
2)minor_version和major_version
    class文件的下面4个字节包含了主、次版本号。对于Java虚拟机来说,版本号确定了特定的class文件格式,通常只有给定主版本号和一系列次版本号后,Java虚拟机才能够读取class文件。
 
3)constant_pool_count和constant_pool
    魔数和版本号后面的是常量池。常量池包含了与文件中类和接口相关的常量。常量池中存储了诸如文字字符串、final变量值、类名和方法名的常量。Java虚拟机把常量池组织为入口列表的形式。在实际列表constant_pool之前,是入口在列表中的计数constant_pool_count。
    每个常量池入口都从一个长度为一个字节的标志开始,这个标志指出了列表中该位置的常量类型。
    每一个标志都有一个相对应的表,表名通过在标志名后加上“_info”后缀来产生。

常量池标志

入 口 类 型

标 志 值

描 述

CONSTANT_Utf8

1

UTF-8编码的Unicode字符串

CONSTANT_Integer

3

int类型字面值

CONSTANT_Float

4

float类型字面值

CONSTANT_Long

5

long类型字面值

CONSTANT_Double

6

double类型字面值

CONSTANT_Class

7

对一个类或接口的符号引用

CONSTANT_String

8

String类型字面值

CONSTANT_Fieldref

9

对一个字段的符号引用

CONSTANT_Methodref

10

对一个类中声明的方法的符号引用

CONSTANT_InterfaceMethodref

11

对一个接口中声明的方法的符号引用

CONSTANT_NameAndType

12

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

 
    除了字面常量(或者说直接量)值以外,常量池还可以容纳下面几种符号引用:
  • 类和接口的全限定名
  • 字段的名称和描述符
  • 方法的名称和描述符
4)access_flags
    紧接常量池后的两个字节称为access_flags,它展示了文件中定义的类或接口的几段信息。

access_flags项的标志位

标 志 名

设置后的含义

设 置 者

ACC_PUBLIC

0x0001

public类型

类和接口

ACC_FINAL

0x0010

类为final类型

只有类

ACC_SUPER

0x0020

使用新型的invokespecial语义

类和接口

ACC_INTERFACE

0x0200

接口类型,不是类类型

所有的接口,没有类

ACC_ABSTRACT

0x0400

abstract类型

所有的接口,部分类

    在access_flags中所有未使用的位都必须由编译器置0,而且Java虚拟机必须忽略它。
 
5)this_class
    接下来的两个字节为this_class项,它是一个对常量池的索引。在this_class位置的常量池入口必须为CONSTANT_Class_info表。该表由两个部分组成——标签和name_index。标签部分是一个具有CONSTANT_Class值的常量,在name_index位置的常量池入口为一个包含了类或接口全限定名的CONSTANT_Utf8_info表。


 
6)super_class
    紧接在this_class之后的是super_class项,它是一个两个字节的常量池索引。在super_class位置的常量池入口是一个指向该类超类全限定名的CONSTANT_Class_info入口。
 
7)interfaces_count和interfaces
    紧接着super_class是interfaces_count。此项的含义为:在文件中由该类直接实现或者由接口所扩展的父接口的数量。在这个计数的后面,是名为interfaces的数组,它包含了对每个由该类或者接口直接实现(注:只包含直接出现在implements、extends子句中的父接口)的父接口的常量池索引。
 
8)fields_count和fields
    紧接在interfaces后面的是对在该类或者接口中所声明的字段的描述。首先是名为fields_count的计数,它是类变量和实例变量的字段的数量总和。在这个计数后面的是不同长度的field_info表的序列(fields_count指出了序列中有多少个field_info表)。在fields列表中,不列出从超类或者父接口继承而来的字段。
 
9)methods_count和methods
    紧接着fields后面的是对在该类或者接口中所声明的方法的描述。只包括在该类或者接口中显式定义的方法。
 
10)attributes_count和attributes
    class文件中最后的部分是属性(attribute),它给出了在该文件中类或者接口所定义的属性的基本信息。
 
特殊字符串
    常量池中容纳的符号引用包括三种特殊的字符串:全限定名、简单名称和描述符。
 
全限定名
    当常量池入口指向类或者接口时,它们给出该类或者接口的全限定名。在class文件中,全限定名中的点用斜线取代了。
 
简单名称
    字段名和方法名以简单名称(非全限定名)形式出现在常量池入口中。
 
描述符
    字段的描述符给出了字段的类型;方法描述符给出了方法的返回值和方法参数的数量、类型以及顺序。
    字段和方法的描述符由如下所示的上下文无关语法定义。该语法中非终结符号用斜体字标出,如FieldType;终结符号使用等宽度字体标出,如BV;星号代表紧接在它前面的符号(中间没有空格)将会出现0次或者多次。
 
FieldDescriptor:
        FieldType
ComponentType:
        FieldType
FieldType:
        BaseType
        ObjectType
        ArrayType
BaseType:
        B
        C
        D
        F
        I
        J
        S
        Z
ObjectType:
        L<classname>;
ArrayType:
        [ComponentType
MethodDescriptor:
        (ParameterDescriptor*)ReturnDescriptor
ParameterDescriptor:
        FieldType
ReturnDescriptor:
        FieldType
        V
 
V        表示方法返回值为void类型
L;      对象类型终结符
[         数组类型终结符
()      方法描述符终结符
 

基本类型终结符

终 结 符

类 型

B

byte

C

char

D

double

F

float

I

int

J

long

S

short

Z

boolean


字段描述符示例

描 述 符

字 段 声 明

I

int i;

[[J

long[][] windingRoad;

[Ljava/lang/Object;

java.lang.Object[] stuff;

Ljava/util/Hashtable;

java.util.Hashtable ht;

[[[Z

boolean[][][] isReady;

 

方法描述符示例

描 述 符

方 法 声 明

()I

int getSize();

()Ljava/lang/String;

String toString();

([Ljava/lang/String;)V

void main(String[] args);

()V

void wait();

(JI)V

void wait(long timeout, int nanos);

(ZILjava/lang/String;II)Z

boolean regionMatches(boolean ignoreCase, int toOffset, String other, int offset, int len);

([BII)I

int read(byte[] b, int off, int len);



常量池
    常量池是一个可变长度cp_info表的有序序列。cp_info表一共有11种类型。

cp_info表的通常形式

类 型

名 称

数 量

描 述

u1

tag

1

表的类型和格式

u1

info

根据tag值决定

 


1)CONSTANT_Utf8_info表
    可变长度的CONSTANT_Utf8_info表使用一种UTF-8格式的变体来存储一个常量字符串。这种类型的表可以存储多种字符串,包括:
  • 文字字符串,如String对象。
  • 被定义的类和接口的全限定名。
  • 被定义的类的超类(如果有的话)的全限定名。
  • 被定义的类和接口的父接口的全限定名。
  • 由类或者接口声明的任意字段的简单名称和描述符。
  • 由类或者接口声明的任意方法的简单名称和描述符。
  • 任何引用的类和接口的全限定名。
  • 任何引用的字段的简单名称和描述符。
  • 任何引用的方法的简单名称和描述符。
  • 与属性相关的字符串。

CONSTANT_Utf8_info表的格式

类 型

名 称

数 量

描 述

u1

tag

1

值为CONSTANT_Utf8(1)

u2

length

1

bytes项的长度(字节数)

u1

bytes

length

按照变体UTF-8格式存储的字符串中的字符


2)CONSTANT_Integer_info表的格式

CONSTANT_Integer_info表的格式

类 型

名 称

数 量

描 述

u1

tag

1

值为CONSTANT_Integer(3)

u4

bytes

1

按照高位在前的格式存储int类型值


3)CONSTANT_Float_info表的格式

CONSTANT_Float_info表的格式

类 型

名 称

数 量

描 述

u1

tag

1

值为CONSTANT_Float(4)

u4

bytes

1

按照高位在前的格式存储float类型值


4)CONSTANT_Long_info表的格式

CONSTANT_Long_info表的格式

类 型

名 称

数 量

描 述

u1

tag

1

值为CONSTANT_Long(5)

u8

bytes

1

按照高位在前的格式存储long类型值


5)CONSTANT_Double_info表的格式

CONSTANT_Double_info表的格式

类 型

名 称

数 量

描 述

u1

tag

1

值为CONSTANT_Double(6)

u8

bytes

1

按照高位在前的格式存储double类型值


6)CONSTANT_Class_info表的格式

CONSTANT_Class_info表的格式

类 型

名 称

数 量

描 述

u1

tag

1

值为CONSTANT_Class(7)

u2

name_index

1

包含类或者接口全限定名的CONSTANT_Utf8_info表的索引


7)CONSTANT_String_info表的格式

CONSTANT_String_info表的格式

类 型

名 称

数 量

描 述

u1

tag

1

值为CONSTANT_String(8)

u2

string_index

1

包含文字字符串值的CONSTANT_Utf8_info表的索引


8)CONSTANT_Fieldref_info表的格式

CONSTANT_Fieldref_info表的格式

类 型

名 称

数 量

描 述

u1

tag

1

值为CONSTANT_Fieldref(9)

u2

class_index

1

声明被引用字段的类或者接口的CONSTANT_Class_info入口的索引

u2

name_and_type_index

1

提供了CONSTANT_NameAndType_info入口的索引,该入口提供了字段的简单名称以及描述符


9)CONSTANT_Methodref_info表的格式

CONSTANT_Methodref_info表的格式

类 型

名 称

数 量

描 述

u1

tag

1

值为CONSTANT_Methodref(10)

u2

class_index

1

声明被引用方法的类的CONSTANT_Class_info入口的索引

u2

name_and_type_index

1

提供了CONSTANT_NameAndType_info入口的索引,该入口提供了方法的简单名称以及描述符


10)CONSTANT_InterfaceMethodref_info表的格式

CONSTANT_InterfaceMethodref_info表的格式

类 型

名 称

数 量

描 述

u1

tag

1

值为CONSTANT_InterfaceMethodref(11)

u2

class_index

1

声明被引用方法的接口的CONSTANT_Class_info入口的索引

u2

name_and_type_index

1

提供了CONSTANT_NameAndType_info入口的索引,该入口提供了方法的简单名称以及描述符


11)CONSTANT_NameAndType_info表的格式

CONSTANT_NameAndType_info表的格式

类 型

名 称

数 量

描 述

u1

tag

1

值为CONSTANT_NameAndType(12)

u2

name_index

1

给出了CONSTANT_Utf8_info入口的索引,该入口给出了字段或者方法的名称

u2

descriptor_index

1

提供了CONSTANT_Utf8_info入口的索引,该入口提供了字段或者方法的描述符

 
字段
    在类或者接口中声明的每一个字段(类变量或者实例变量)都由class文件中的一个名为field_info的可变长度的表进行描述。

field_info表的格式

类 型

名 称

数 量

描 述

u2

access_flags

1

见下方的表

u2

name_index

1

提供了给出字段简单名称(不是全限定名)的CONSTANT_Utf8_info入口的索引

u2

descriptor_index

1

提供了给出字段描述符的CONSTANT_Utf8_info入口的索引

u2

atrributes_count

1

attributes_count指出列表中attribute_info表的数量

attribute_info

atrributes

atrributes_count

由多个attribute_info表组成的列表


field_info表中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_PUBLIC、ACC_PRIVATE、ACC_PROTECTED这三个标志中的一个。ACC_FINAL和ACC_VOLATILE不能同时设置。所有接口中声明的字段必须有且只能有ACC_PUBLIC、ACC_STATIC和ACC_FINAL这三种标志。
 
方法
    在class文件中,每个在类和接口中声明的方法,或者由编译器产生的方法,都由一个可变长度的method_info表来描述。

method_info表的格式

类 型

名 称

数 量

描 述

u2

access_flags

1

见下方的表

u2

name_index

1

提供了给出方法简单名称(不是全限定名)的CONSTANT_Utf8_info入口的索引

u2

descriptor_index

1

提供了给出方法描述符的CONSTANT_Utf8_info入口的索引

u2

atrributes_count

1

attributes_count指出列表中attribute_info表的数量

attribute_info

atrributes

atrributes_count

由多个attribute_info表组成的列表


method_info表中access_flags项的标志

标 志 名 称

设 定 含 义

设 定 者

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_NATIVE

0x0100

方法设为native

只有类

ACC_ABSTRACT

0x0400

方法设为abstract

类和所有的接口方法

ACC_STRICT

0x0800

方法设为strictFP

类和接口的<clinit>方法


 
属性
    属性在Java class文件中多处出现。它们可以出现在ClassFile、field_info、method_info和Code_attribute表中。Code_attribute表本身即为一个属性。
    Java虚拟机规范定义了9种属性。为了正确地解释Java class文件,所有Java虚拟机实现都必须能够识别下列三种属性:Code,ConstantValue和Exception。为了正确地实现Java和Java 2平台类库,虚拟机实现必须能够识别InnerClasses和Synthetic属性,但可以自主选择究竟是识别还是忽略其他一些预定义的属性。
 

由规范定义的attribute_info表的类型

名 称

使 用 者

描 述

Code

method_info

方法的字节码和其他数据

ConstantValue

field_info

final变量的值

Deprecated

field_info、method_info

字段或者方法被禁用的指示符

Exceptions

method_info

方法可能抛出的可被检测的异常

InnerClasses

ClassFile

内部、外部类的列表

LineNumberTable

Code_attribute

方法的行号与字节码的映射

LocalVariableTable

Code_attribute

方法的局部变量的描述

SourceFile

ClassFile

源文件名

Synthetic

field_info、method_info

编译器产生的字段或者方法的指示符


attribute_info表的格式

类 型

名 称

数 量

描 述

u2

attribute_name_index

1

给出了包含属性名称的CONSTANT_Utf8入口的常量池中的索引

u4

attribute_length

1

给出了属性数据的长度(以字节计)

u1

info

attribute_length

包含属性数据


 
最后,简单总结一下Java class文件:
  • 一个class文件中只能包含一个类或者接口
  • 尽管class文件与Java语言结构相关,但它并不一定必须与Java语言相关
  • Java class文件是8位字节的二进制流
  • 数据项按顺序存储
  • 文件紧凑
  • 多字节的项按照高位在前的格式存储
  • 常量池至关重要,包含了与class文件中类和接口相关的常量
    
    在此说明,本系列文章的内容均出自《深入理解Java虚拟机》一书,除了极少数的“注”或对内容的裁剪整理外,内容原则上与原书保持一致。由于这是一本原理性的书籍,本人不想因为自己能力与理解的问题对大家造成误解,所以除了对原书内容的裁剪整理之外,基本不做任何内容的延伸思考与扩展。
    另外,如果您对本系列文章的内容感兴趣,建议您去阅读原版书籍,谢谢!
 
(转载请注明来源:http://zhanjia.iteye.com/blog/1857709)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值