java字节码编程技术(10/10)-class文件格式详解
  TEZNKK3IfmPf 2024年03月29日 36 0

1.1、标准定义

ClassFile { 
    u4 magic;  // 魔法数字,表明当前文件是.class文件,固定0xCAFEBABE
    u2 minor_version; // 分别为Class文件的java副版本和主版本,如果超出了jvm的版本范围,则不会执行此文件
    u2 major_version; 1.4对应48,java9对应53
    u2 constant_pool_count; // 常量池计数
    cp_info constant_pool[constant_pool_count-1];  // 常量池内容,常量池中的许多入口都指向了其它常量池中的其它入口
    u2 access_flags; // 类访问标识
    u2 this_class; // 当前类, this_class存的是当前类的名称在常量池里的索引,指向常量池中的CONSTANT_Class
    u2 super_class; // 父类,super_class存的是父类的名称在常量池里的索引,指向常量池中的CONSTANT_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]; // 各种属性
}

1.2、详细说明

constant_pool常量池内容

CONSTANT_Utf8:utf-8编码的unicode字符串
CONSTANT_Integer:boolean, byte,short,char都会当会int来处理
CONSTANT_Float:以下三种类型占用了常量池2个位置,分高位和低位之分
CONSTANT_Long:
CONSTANT_Double:
CONSTANT_Class:对一个类或接口的符号引用
CONSTANT_String: 这里保存的是一个指向CONSTANT_Utf8的指针,在CONSTANT_Utf8才保存真正的字符串内容
CONSTANT_Fieldref:对一个字段的符号引用
CONSTANT_Methodref:对一个类中声明的方法的符号引用
CONSTANT_InterfaceMethodref:对一个接口中声明的符号引用
CONSTANT_NameAndTyp:对一个字段或方法的部分符号引用

access_flag类的访问标记

ACC_PUBLIC:
ACC_FINAL:
ACC_SUPER:表示当用invokespecial指令来调用父类的方法时需要特殊处理
ACC_INTERFACE:
ACC_ABSTRACT:
ACC_SYNTHETIC:
ACC_ANNOTATION:
ACC_ENUM:

this_class、super_class、interfaces

这三个的内容是指向常量池的索引,大体结构如下:常量池入口的索引是从1开始,递增的。

java字节码编程技术(10/10)-class文件格式详解

  • super_class:只有一个值直接指向直接超类,如果没有超类则指向java.lang.Object;
  • interfaces:按类代码写法顺序的接口数组;

fields

只列出本类中定义的字段,接口和超类的字段不在此处展示。有些会在此处展增添详细信息,有些会保存一个引用地址指向常量池;

attributes

定义了在此文件中类或接口所定义的属性的基本信息。

  • SourceCode:
  • InnerClasses:

1.3、例子

可用javap -c .class命令反编.class文件查看二进制文件;

class Example1a {
  static int size;
  Example1a();
  static {};
}
//-------
Compiled from "Example1a.java"
class Example1a {
  static int size;

  Example1a();
    Code:
       0: aload_0
       1: invokespecial #4                  // Method java/lang/Object."<init>":()V
       4: return

  static {};
    Code:
       0: iconst_3
       1: invokestatic  #5                  // Method java/lang/Math.random:()D
       4: ldc2_w        #7                  // double 5.0d
       7: dmul
       8: d2i
       9: imul
      10: putstatic     #6                  // Field size:I
      13: return
}

 

二、常量池

常量池的通用格式{tag:类型标识,共11种,info:根据tag的不同而不同},所有的数值类型都不存储符号信息;如果基本类型不初始化是不会在常量池中有信息的,对象类型都会创建,包括String。

tag=1 CONSTANT_Utf8 utf-8编码的unicode字符串

CONSTANT_UTF8{
u1 tag:
u2 length:
u1 bytes[length]
}

tag=3 CONSTANT_Integer boolean, byte,short,char都会当会int来处理

CONSTANT_Integer{
u1 tag:
u4 bytes
}

tag=4 CONSTANT_Float 以下三种类型占用了常量池2个位置,分高位和低位之分

CONSTANT_Float{
u1 tag:
u4 bytes;
}

tag=5 CONSTANT_Long

CONSTANT_Long{
u1 tag:
u4 high_bytes:
u4 low_bytes:
}

tag=6 CONSTANT_Double

CONSTANT_Double{
u1 tag:
u4 high_bytes:
u4 low_bytes:
}

tag=7 CONSTANT_Class:对一个类或接口的符号引用

name_index的值由指向常量池的索引以及类的全路径两部分组成,索引指向一个utf-8地址,其主要是描述类全路径信息的
CONSTANT_Class{
u1 tag:
u2 name_index:55 -CONSTANT_UTF8:com/jvm/demo
}

tag=8 CONSTANT_String: 一个指向CONSTANT_Utf8的指针,在CONSTANT_Utf8才保存真正的字符串内容

CONSTANT_String{
u1 tag:
u2 string_index:55 -CONSTANT_UTF8:...
}

tag=9 CONSTANT_Fieldref:对一个字段的符号引用

CONSTANT_Fieldref{
u1 tag:
u2 class_index:55 -CONSTANT_Class:com.jvm.demo
u2 name_and_type_index:25 -CONSTANT_NameAndType:money, J
}

基本类型限定符

注意上面例子中最后的J,它代表字段的类型,B=byte、C=char、D=double、F=float、I=int、J=long、S=short、Z=double;

字段描述

I=int、[[=[][]二维数组、[Ljava/lang=对象数组、Ljava/lang=对象、[[[=三维数组

tag=10 CONSTANT_Methodref:对一个类中声明的方法的符号引用,不包含超类和接口

CONSTANT_MethodRef{
u1 tag:
u2 class_index:55 -CONSTANT_Class:com.jvm.demo
u2 name_and_type_index:25 -CONSTANT_NameAndType:<init>, ()V
}

正常来讲在常量池中只会生成两个类引用,一个是默认的无参构造函数,另一个是main方法。对于自定义的方法不会保存在常量池里

  • com.jvm.superclass.DemoSuper1.<init>, parameter = (), returns = void
  • java.io.PrintStream.println, parameter = (java.lang.String), returns = void

方法描述

  • ()I = 无参方法,返回值为int
  • ()Ljava/lang/String 无参方法,返回值为String
  • ()V = 无参方法,返回值为void
  • (ZI)V = 参数为double, int。返回值为void

tag=11 CONSTANT_InterfaceMethodref:对一个接口中声明的方法符号引用,只是文件为接口类型时才生效

CONSTANT_InterfaceMethodRef{
u1 tag:
u2 class_index:55 -CONSTANT_Class:com.jvm.demo
u2 name_and_type_index:25 -CONSTANT_NameAndType:<init>, ()V
}

但在现在一般这个属性由class来代替了。

tag=12 CONSTANT_NameAndType:对一个字段或方法的详细描述

CONSTANT_String{
u1 tag:
u2 name_index:55 -CONSTANT_Utf8:name
u2 descriptor_index:25 -CONSTANT_Utf8:Ljava/lang/String;
}

三、fields、method、attributes

3.1、fields字段

field{
u2	access_flags:2, private--修饰符
u2	name_index:20 -constant_utf8:userName
u2	descriptor_index:21 -constant_utf8:Ljava/lang/String;
u2	attributes_count:
attributes_info:
}

access_flags

可选的有public, private, protected, static, final, volatile, transient,其中第一个数字2只是一个计算标识,没有其它的对应关系;

3.2、methods方法

field{
u2	access_flags:2, private--修饰符
u2	name_index:20 -constant_utf8:userName--方法名称
u2	descriptor_index: 方法描述
u2	attributes_count:属性
attributes_info:
}

access_flags

可选的有public, private, protected, static, final, synchronized, native, abstract, strict,其中第一个数字2只是一个计算标识,没有其它的对应关系;

3.3、attributes属性

它可以出现在clasFile、field、method、attributes之中,所有的jvm都要能识别code、constantValue、exception三种属性。还要能够识别区分java1和java2平台的innerClasses、Synthetic属性。

类型

使用者

描述

code

method

方法的字节码

ConstantValue

field

final变量的值

Deprecated

field, method

字段或方法被禁用的指标符

Exceptions

classFile

需查异常

InnerClasses

code

内部类、外部类列表

LineNumberTable

code

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

LocalVariableTable

classFile

方法的局部变量

SourceFile

classFile

源文件名

Sythetic

field, method

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

通用格式

attribute{
u2 	attribute_name_index:---属性名称
u4	attribute_length :--属性长度
u1	info:--属性详细,就是上面中的类型决定
}

code--用来描述方法

attribute{
u2 	attribute_name_index:---属性名称
u4	attribute_length :--属性长度
u2	max_stack:---此方法的操作数栈的最大字节数
u2	max_locals:---此方法的局部变量最大的存储空间的长度
u4	code_lenght:---此方法字节流的长度
u1 	code:---此方法字节流内容
u2	exception_table_length:
exception_table
u2	attributes_count
attributes
}
exception_table{
u2 	start_pc--从代码数组起始处到异常处理器起始处的偏移量
u2 	end_pc--从代码数组起始处到异常处理器结束后一个字节的偏移量
u2 	handler_pc--从代码数组起始处到异常处理器的第一条指令的偏移量
u2 	catch_type--指定异常处理器所捕获的异常类型的常量池索引,当值为0时表示finally语句
}

ConstantValue--用于描述常量字段

ConstantValue{
        attribute_name_index:
        attribute_length:
        constantvalue_index:
}

Deprecated--用来解释@deprecated注解

Deprecated{
        attribute_name_index:
        attribute_length:
}

Exceptions--用来描述类一般是main函数,方法的在code里面描述

Exceptions{
    u2   attribute_name_index:
    u4   attribute_length:
    u2   number_of_exceptions
    u2   exception_index_table--指向一个常量池的索引 
}

InnerClasses--内部类、外部类列表

InnerClasses_attribute{
    u2   attribute_name_index:
    u4   attribute_length:
    u2   number_of_classes
           classes--指向一个常量池的索引 
}
Inner_class{
    u2   inner_class_info_index:
    u2   outer_class_info_index:
    u2   inner_name_index:
    u2   inner_class_access_flags:
}

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

LineNumberTable{
    u2   attribute_name_index:
    u4   attribute_length:
    u2   line_number_table_length
           line_number_table 
}
line_number_table{
    u2   start_pc--新行开始时代码数组的偏移量
    u2   line_number--从start_pc开始的行号
}

LocalVariableTable--方法的局部变量

LocalVariableTable{
    u2   attribute_name_index:
    u4   attribute_length:
    u2  local_variable_table_length
           local_variable_table 
}
 local_variable_table {
    u2   start_pc--指令开始位置的偏移量
    u2   length
    u2   name_index:指向常量池入口
    u2  descriptor_index:指向常量池入口
    u2  index:此方法栈帧中局部变量部分的索引,如果是long和double由占2个字节
}

SourceFile---用于描述class

SourceFile{
    u2   attribute_name_index:
    u4   attribute_length:
    u2  sourcefile_index:
}

Sythetic---编译器产生的字段或方法的指示符,用于处理@synthetic注解

Sythetic{
    u2   attribute_name_index:
    u4   attribute_length:
}
【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2024年03月29日 0

暂无评论

推荐阅读
  TEZNKK3IfmPf   19天前   43   0   0 java
  TEZNKK3IfmPf   2024年05月31日   54   0   0 java
TEZNKK3IfmPf