先以最简单的类结构介绍字节码文件的解析
public class Test { private static int a = 12; public static void main(String[] args) { System.out.println("dsafad"); } }
通过javap -v Test.class命令反编译字节码信息如下:该信息是已经把二进制数据反编译后的结果
public class com.tongweb.uconsole.Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #7.#24 // java/lang/Object."<init>":()V
#2 = Fieldref #25.#26 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #27 // dsafad
#4 = Methodref #28.#29 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Fieldref #6.#30 // com/tongweb/uconsole/Test.a:I
#6 = Class #31 // com/tongweb/uconsole/Test
#7 = Class #32 // java/lang/Object
#8 = Utf8 a
#9 = Utf8 I
#10 = Utf8 <init>
#11 = Utf8 ()V
#12 = Utf8 Code
#13 = Utf8 LineNumberTable
#14 = Utf8 LocalVariableTable
#15 = Utf8 this
#16 = Utf8 Lcom/tongweb/uconsole/Test;
#17 = Utf8 main
#18 = Utf8 ([Ljava/lang/String;)V
#19 = Utf8 args
#20 = Utf8 [Ljava/lang/String;
#21 = Utf8 <clinit>
#22 = Utf8 SourceFile
#23 = Utf8 Test.java
#24 = NameAndType #10:#11 // "<init>":()V
#25 = Class #33 // java/lang/System
#26 = NameAndType #34:#35 // out:Ljava/io/PrintStream;
#27 = Utf8 dsafad
#28 = Class #36 // java/io/PrintStream
#29 = NameAndType #37:#38 // println:(Ljava/lang/String;)V
#30 = NameAndType #8:#9 // a:I
#31 = Utf8 com/tongweb/uconsole/Test
#32 = Utf8 java/lang/Object
#33 = Utf8 java/lang/System
#34 = Utf8 out
#35 = Utf8 Ljava/io/PrintStream;
#36 = Utf8 java/io/PrintStream
#37 = Utf8 println
#38 = Utf8 (Ljava/lang/String;)V
{
public com.tongweb.uconsole.Test();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/tongweb/uconsole/Test;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String dsafad
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 8: 0
line 9: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: bipush 12
2: putstatic #5 // Field a:I
5: return
LineNumberTable:
line 5: 0
}
java字节码结构如下:
字节码文件标识说明
下面我们将参照java字节码整体结构来解析字节码文件
魔数为固定值在上面可能没有展示
此版本号:0
主版本号:52 【1.8】
类的访问控制权限: ACC_PUBLIC, ACC_SUPER
常量池大小:39 但实际上常量池里面是要减1
常量池:
#1 = Methodref #7.#24 // java/lang/Object."<init>":()V
表示方法的引用,#7 #24指向常量池中的位置常量
#2 = Fieldref #25.#26 // java/lang/System.out:Ljava/io/PrintStream;
表示字段引用 #25 #26 指向常量池中25,26的位置常量
#3 = String #27
String类型,指向常量池中27的位置
#4 = Methodref #28.#29 // java/io/PrintStream.println:(Ljava/lang/String;)V
方法引用指向28,29号位置
#5 = Fieldref #6.#30 // com/tongweb/uconsole/Test.a:I
字段引用,指向6,30号位置,为整数类型a
#6 = Class #31 // com/tongweb/uconsole/Test
Class 指向31号位置
#7 = Class #32 // java/lang/Object
这个应该是父类信息,指向32号位置
#8 = Utf8 a
变量名
#9 = Utf8 I
变量类型
#10 = Utf8 <init>
初始化方法
#11 = Utf8 ()V
初始化方法,返回为空
#12 = Utf8 Code
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/tongweb/uconsole/Test;
代码
invokespecial指向本实例方法,aload_0表示对this的操作
#13 = Utf8 LineNumberTable
源码行号和字节码行号的对应关系
#14 = Utf8 LocalVariableTable
本地变量表
#15 = Utf8 this
this对象
#16 = Utf8 Lcom/tongweb/uconsole/Test;
#17 = Utf8 main
#18 = Utf8 ([Ljava/lang/String;)V
#19 = Utf8 args
#20 = Utf8 [Ljava/lang/String;
#21 = Utf8 <clinit>
#22 = Utf8 SourceFile
#23 = Utf8 Test.java
以下是解析main方法和clint静态方法,就不一一说明了,原理是一样的(这个地方我再main中声明了个局部变量,并进行加一操作)
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: iconst_0
1: istore_1
2: iinc 1, 1
5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #3 // String dsafad
10: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: return
LineNumberTable:
line 8: 0
line 9: 2
line 10: 5
line 11: 13
LocalVariableTable:
Start Length Slot Name Signature
0 14 0 args [Ljava/lang/String;
2 12 1 b I
main方法
方法的描述:(参数类型)返回值类型
访问标志:静态,公共
代码:
stack: 最大操作数栈,locals本地变量数量,args_size,形参数量
iconst_0: 将常量压入堆栈
istore_1: 弹出1号位置元素,0号位置是this
iinc 1,1: 增1
getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
获得常量池中2号位置的静态变量
ldc #3 // String dsafad
将常量压入堆栈
invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
调用实例方法
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: bipush 12
2: putstatic #5 // Field a:I
5: return
LineNumberTable:
line 5: 0
静态代码块的执行