概述:

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

其实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 TestClass
Classfile /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_SUPER
Constant 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之后编译出来的类的这个标志为true
ACC_INTERFACE0x0200标志这个是一个接口
ACC_ABSTRACT0x0400是否为abstract类型,对于接口或抽象类来说,此标志值为true,其他值为false
ACC_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_flags1
u2name_index1
u2descriptor_index1
u2attributes_count1
attribute_infoattributesattributes_count

字段访问标志

字段访问标志
标志名称标志值含义
ACC_PUBLIC0x0001字段是否public
ACC_PRIVATE0x0002字段是否private
ACC_PROTECTED0x0004字段是否protected
ACC_STATIC0x0008字段是否static
ACC_FINAL0x0010字段是否为final
ACC_VOLATILE0x0040字段是否volatile
ACC_TRANSIENT0x0080字段是否transient
ACC_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基本类型byte
C基本类型char
D基本类型double
F基本类型float
I基本类型int
J基本类型long
S基本类型short
Z基本类型boolean
V特殊类型void
L对象类型 如 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方法是否public
ACC_PRIVATE0x0002方法是否private
ACC_PROTECTED0x0004方法是否protected
ACC_STATIC0x0008方法是否static
ACC_FINAL0x0010方法是否为final
ACC_SYNCHRONIZED0x0040方法是否sychronized
ACC_BRIDGE0x0080方法是否是由编译器产生的桥接方法
ACC_VARARGS0x1000方法是否接受不定参数
ACC_NATIVE0x4000方法是否为native
ACC_ABSTRACT0x0080方法是否为Abstract
ACC_STRICT0x1000方法是否为strictfp
ACC_SYNTHETIC0x4000方法是否有编译器自动产生

方法中代码存在于code 中

2.8属性表集合

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

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

属性表结构
类型名称数量
u2attribute_name_index1
u2attribute_length1
u1infoattribute_length

2.8.1 Code属性

Code属性表的结构
类型名称数量
u2attribute_name_index1
u4attribute_length1
u2max_stack1
u2max_locals1
u4code_length1
u1codecode_length
u2exception_table_length1
exception_infoexception_tableexception_table_length
u2attributes_count1
attribute_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_pc1
u2end_pc1
u2handler_pc1
u2catch_type1
JVM虚拟机基于栈模型

2.8.2 Exceptions属性

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

属性表结构
类型名称数量
u2attribute_name_index1
u4attribute_length1
u2number_of_exceptions1
u2exception_index_tablenumber_of_exception

2.8.3、LineNumberTable属性

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

LineNumberTable属性结构
类型名称数量
u2attribute_name_index1
u4attribute_length1
u2line_number_table_length1
line_number_infoline_number_tableline_number_table_length

2.8.4 、LocalVariableTable属性

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

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

LocalVariableTable属性结构
类型名称数量
u2attribute_name_index1
u4attribute_length1
u2local_variable_table_length1
local_variable_infolocal_variable_tablelocal_variable_table_length

local_variable_info项目结构
类型名称数量
u2start_pc1
u2length1
u2name_index1
u2descriptor_index1
u2index1

2.8.5/ SourceFile属性

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

SourceFile属性结构
类型名称数量
u2attribute_name_index1
u4attribute_length1
u2sourcefile_index1


2.8.6、ConstantValue属性

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

ConstantValue属性结构
类型名称数量
u2attribute_name_index1
u4attribute_length1
u2constantvalue_index1

2.8.7 innerClasses属性

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

InnerClasses属性结构
类型名称数量
u2attribute_name_index1
u4attribute_length1
u2number_of_classes1
inner_classes_infoinner_classesnumber_of_classes

inner_classes_info表结构
类型名称数量
u2inner_class_info_index1
u2outer_class_info_index1
u2inner_name_index1
u2inner_class_access_flags1

inner_class_access_flags是内部类的访问标志

inner_class_access_flags标志
标志名称标志值含义
ACC_PUBLIC0x0001内部类是否为public
ACC_PRIVATE0x0002内部类是否为private
ACC_PROTECTED0x0004内部类是否为protected
ACC_STATIC0x0008内部类是否为static
ACC_FINAL0x0010内部类是否为final
ACC_SYNCHRONIZED0x0020内部类是否为synchronized
ACC_ABSTRACT0x0400内部类是否为abstract
ACC_SYNTHETIC0x1000内部类是否并非用户代码产生的
ACC_ANNOTATION0x2000内部类是否一个注解
ACC_ENUM0x4000内部类是否是一个枚举

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

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐