前言:
现在朋友们对“oracle数据字典翻译中文”大约比较看重,看官们都需要学习一些“oracle数据字典翻译中文”的相关资讯。那么小编也在网上搜集了一些对于“oracle数据字典翻译中文””的相关知识,希望各位老铁们能喜欢,同学们一起来学习一下吧!作为一个大龄程序员,从开始学习java的时候就开始接触class文件,作为一个拿来即用主义的人,仅需要知道怎么跑起程序即可完成所有工作,从来没有想到看什么是字节码,无奈Java太内卷了,只能尝试花了一天,去看懂字节码。
啥是class文件
Class文件格式采用一种类似于C语言结构体的方式进行数据存储,这种结构中只有两种数据类型:无符号数和表。
无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和 8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照 UTF-8编码构成字符串值。
表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性地以“_info”结尾。表用于描述有层次关系的复合结构的数据,整个Class 文件本质上就是一张表。由于表没有固定长度,所以通常会在其前面加上个数说明。
为了避免与类和类实例等字段的混淆,描述类文件格式的结构的内容被称为项。连续的项是按顺序存储在类文件中,且没有填充或对齐。
class文件结构与每项占用的字节长度
ClassFile { u4 magic; //魔数,标识为Class文件 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]; //属性表}
从javac对源码进行编译后得到的class文件,打开后类似于乱码的文件,实际上内容是按照class的结构以每一项类似数组按顺序排列。
例如 [u4 magic][u2 minor_version][u2 major_version][...][...][...][...][...][...][...]
参考ClassFile里面的结构,根据字节码文件按照对应的字节数进行翻译。
通过javap -v -p .class文件也能得到字节码翻译后的文件
开始痛苦之旅
魔数(2字节)
提供标识类文件格式的魔术数字;它的值为0xCAFEBABE。
主版本号(2字节)与次版本号(2字节)
决定class文件的版本号,通常以主版本号.次版本号作为class文件的版本,高版本的字节码文件不能在低版本的虚拟机中运行,Java8对应的版本号为52.0,16进制34代表是52。
常量池计数器(2字节)
常量池计数器值为常量池长度 + 1 ,即常量池长度为10 ,那么常量池计数器值为11。该常量池计数器 26即常量池计数器为38。
常量池表
常量池主要存放两大常量:字面量和符号引用。字面量比较接近于Java语言层面的常量概念,如文本字符串、声明为final的常量值。而符号引用则属于编译原理方面的概念。
常量池中每一项常量都是一个表 ,开始的第一位是一个 u1 类型的标志位 【tag】 来表示常量类型。
#常量池结构cp_info { u1 tag; u1 info[]; }
tag代表的含义:
类型
标志(tag)
描述
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
表示一个动态方法调用点
常量池第一项中0A等于10进制里面的10,根据tag对照表中属于CONSTANT_Methodref_info结构一共5个字节。
CONSTANT_Methodref_info { u1 tag; u2 class_index; u2 name_and_type_index; }
{ tag:CONSTANT_Methodref_info , class_index:6 , name_and_type_index:24}
常量池表中第7项中,tag = 01 ,length = 8 ,bytes长度 = 8 , 总长度 1 + 2 + 8 = 11
每项常量池都以这种方式去翻译得出下表,一共37个项,证明常量池表长度 + 1 = 常量池计数器的值。
#1 = Methodref #6.#24 // java/lang/Object."<init>":()V #2 = Fieldref #5.#25 // simple02/ByteCodeTest.fieldInt:I #3 = Fieldref #26.#27 // java/lang/System.out:Ljava/io/PrintStream; #4 = Methodref #28.#29 // java/io/PrintStream.println:(I)V #5 = Class #30 // simple02/ByteCodeTest #6 = Class #31 // java/lang/Object #7 = Utf8 fieldInt #8 = Utf8 I #9 = Utf8 <init> #10 = Utf8 ()V #11 = Utf8 Code #12 = Utf8 LineNumberTable #13 = Utf8 LocalVariableTable #14 = Utf8 this #15 = Utf8 Lsimple02/ByteCodeTest; #16 = Utf8 method01 #17 = Utf8 ()I #18 = Utf8 main #19 = Utf8 ([Ljava/lang/String;)V #20 = Utf8 args #21 = Utf8 [Ljava/lang/String; #22 = Utf8 SourceFile #23 = Utf8 ByteCodeTest.java #24 = NameAndType #9:#10 // "<init>":()V #25 = NameAndType #7:#8 // fieldInt:I #26 = Class #32 // java/lang/System #27 = NameAndType #33:#34 // out:Ljava/io/PrintStream; #28 = Class #35 // java/io/PrintStream #29 = NameAndType #36:#37 // println:(I)V #30 = Utf8 simple02/ByteCodeTest #31 = Utf8 java/lang/Object #32 = Utf8 java/lang/System #33 = Utf8 out #34 = Utf8 Ljava/io/PrintStream; #35 = Utf8 java/io/PrintStream #36 = Utf8 println #37 = Utf8 (I)V
此时,正常情况需要停下休息,去治疗一下眼睛了。
访问权限(2字节)
访问标识用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口;是否定义为 public类型,是否定义为 abstract类型;如果是类的话,是否被声明为final等。
类索引(2字节)
该索引指向的字面量格式文件的全限定名,如:java/lang/Object
父类索引(2字节)
该索引指向常量池中CONSTANT_Class_info结构。
接口计数器(2字节)
为类实现的接口数量
接口索引表
该索引集合每项指向常量池中CONSTANT_Class_info结构。
访问标识 21 即 ACC_PUBLIC , ACC_SUPER
类索引指向常量池中#5 字面值 =simple02/ByteCodeTest
父类索引指向常量池中#6 字面值 =java/lang/Object
接口计数器=0,所以后面没有接口索引表
字段计数器(2字节)
描述类中有多少字段。
字段索引集合
用于描述接口或类中声明的变量。字段(field)包括类级变量以及实例级变量,但是不包括方法内部、代码块内部声明的局部变量。
字段叫什么名字、字段被定义为什么数据类型,这些都是无法固定的,只能引用常量池中的常量来描述。
它指向常量池索引集合,它描述了每个字段的完整信息。比如字段的标识符、访问修饰符(public、private或protected)、是类变量还是实例变量(static修饰符)、是否是常量(final修饰符)等。
#字段结构field_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; }
字段计数器 = 1 ,字段表项长度1,字段标识符 = 0x0002 即ACC_PRIVATE,字段名索引=#7字面量=fieldInt,描述=#8 字面量I 即基本数据类型int,attributes_count = 0
方法计数器(2字节)
表示该类中有多少个方法,但不包括超类或超接口中的方法数量。
方法表
表中的每个成员都必须是一个method_info结构,用于表示当前类或接口中某个方法的完整描述。
如果某个method_info结构的access_flags项既没有设置 ACC_NATIVE 标志也没有设置ACC_ABSTRACT标志,那么该结构中也应包含实现这个方法所用的Java虚拟机指令。
method_info结构可以表示类和接口中定义的所有方法,包括实例方法、类方法、实例初始化方法和类或接口初始化方法·方法表的结构实际跟字段表是一样的:
method_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; }
根据字节码,方法表中第一个方法有一个属性,属性指向的字面量是Code,其结构
Code_attribute { u2 attribute_name_index; u4 attribute_length; u2 max_stack; u2 max_locals; u4 code_length; u1 code[code_length]; u2 exception_table_length; { u2 start_pc; u2 end_pc; u2 handler_pc; u2 catch_type; } exception_table[exception_table_length]; u2 attributes_count; attribute_info attributes[attributes_count];}
code[]就是属于虚拟机的指令,它由一个字节长度的、代表着某种特定操作含义的数字(称为操作码,Opcode)以及跟随其后的零至多个代表此操作所需参数(称为操作数,Operands)而构成。由于Java虚拟机采用面向操作数栈而不是寄存器的结构,所以大多数的指令都不包含操作数,只有一个操作码。
由于限制了Java 虚拟机操作码的长度为一个字节(即0~255),这意味着指令集的操作码总数不可能超过256条。
对照字节码指令索引,一个一个查字典方式去标识翻译命令,与javap的文件对比验证是否有误。
翻译后变成如下代码 public simple02.ByteCodeTest(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: iconst_1 6: putfield #2 // Field fieldInt:I 9: return LineNumberTable: line 3: 0 line 5: 4 LocalVariableTable: Start Length Slot Name Signature 0 10 0 this Lsimple02/ByteCodeTest;
终于到最后了。。。。。
attributes_count 、 attributes[]
属性表集合,指的是class文件所携带的辅助信息,比如该class文件的源文件的名称。以及任何带有RetentionPolicy.CLASS 或者RetentionPolicy.RUNTIME的注解。这类信息通常被用于Java虚拟机的验证和运行,以及Java程序的调试,一般无须深入了解。
此外,字段表、方法表都可以有自己的属性表。用于描述某些场景专有的信息。
属性表集合的限制没有那么严格,不再要求各个属性表具有严格的顺序,并且只要不与已有的属性名重复,任何人实现的编译器都可以向属性表中写入自己定义的属性信息,但]ava虚拟机运行时会忽略掉它不认识的属性。
名字索引得出属性是属于SourceFile结构,定长占8个字节,指向常量池中#23,字面值为ByteCodeTest.java
#属性结构attribute_info { u2 attribute_name_index; u4 attribute_length; u1 info[attribute_length];}#SourceFile结构SourceFile_attribute { u2 attribute_name_index; u4 attribute_length; u2 sourcefile_index;}
总结
class文件的结构以每一项类似数组按顺序排列。[u4 magic][u2 minor_version][u2 major_version][...][...][...][...][...][...][...]
每项都有对应的结构,根据【java虚拟机规范】文档,好似查字典一样去解析即可。
文档目录
标签: #oracle数据字典翻译中文