1、解析字节码的作用
- 通过反编译生成的字节码文件,我们可以深入的了解java代码的工作机制。但是,自己分析类文件结构太麻烦了!除了使用第三方的jclasslib工具之外,oracle官方也提供了工具:javap。
- javap是jdk自带的反解析工具。它的作用就是根据class字节码文件,反解析出当前类对应的code区(字节码指令)、局部变量表、异常表和代码行偏移量映射表、常量池等信息。
- 通过局部变量表,我们可以查看局部变量的作用域范围、所在槽位等信息,甚至可以看到槽位复用等信息。
2、javac -g操作
解析字节码文件得到的信息中,有些信息(如局部变量表、指令和代码行偏移量映射表、常量池中方法的参数名称等等)需要在使用javac编译成class文件时,指定参数才能输出。
比如,你直接javac xx.java,就不会在生成对应的局部变量表等信息,如果你使用javac -g xx.java就可以生成所有相关信息了。如果你使用的eclipse或IDEA,则默认情况下,eclipse、IDEA在编译时会帮你生成局部变量表、指令和代码行偏移量映射表等信息的。3、javap的使用
javap的用法格式:
javap
其中,classes就是你要反编译的class文件。
在命令行中直接输入javap或javap -help可以看到javap的options有如下选项:


4、使用举例
public class JavapTest{private int num;boolean flag;protected char gender;public String info;public static final int COUNTS = 1;static{String url = "www.atguigu.com";}{info = "java";}public JavapTest(){}private JavapTest(boolean flag){this.flag = flag;}private void methodPrivate(){}int getNum(int i){return num + i;}protected char shwoGender(){return gender;}public void shwoInfo(){int i = 10;System.out.println(info + i);}}
Classfile /F:/home/idea/JavapTest.class //字节码文件所属路径Last modified 2021-4-26; size 1130 bytes //最后修改时间,字节码文件的大小MD5 checksum 206819f6dea876b5ebbd2afa2aaa3fe1 //MD5散列值Compiled from "JavapTest.java" //源文件的名称public class JavapTestminor version: 0 //副版本major version: 52 //主版本flags: ACC_PUBLIC, ACC_SUPER //访问标识Constant pool: //常量池#1 = Methodref #16.#42 // java/lang/Object."<init>":()V#2 = String #43 // java#3 = Fieldref #15.#44 // JavapTest.info:Ljava/lang/String;#4 = Fieldref #15.#45 // JavapTest.flag:Z#5 = Fieldref #15.#46 // JavapTest.num:I#6 = Fieldref #15.#47 // JavapTest.gender:C#7 = Fieldref #48.#49 // java/lang/System.out:Ljava/io/PrintStream;#8 = Class #50 // java/lang/StringBuilder#9 = Methodref #8.#42 // java/lang/StringBuilder."<init>":()V#10 = Methodref #8.#51 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;#11 = Methodref #8.#52 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;#12 = Methodref #8.#53 // java/lang/StringBuilder.toString:()Ljava/lang/String;#13 = Methodref #54.#55 // java/io/PrintStream.println:(Ljava/lang/String;)V#14 = String #56 // www.atguigu.com#15 = Class #57 // JavapTest#16 = Class #58 // java/lang/Object#17 = Utf8 num#18 = Utf8 I#19 = Utf8 flag#20 = Utf8 Z#21 = Utf8 gender#22 = Utf8 C#23 = Utf8 info#24 = Utf8 Ljava/lang/String;#25 = Utf8 COUNTS#26 = Utf8 ConstantValue#27 = Integer 1#28 = Utf8 <init>#29 = Utf8 ()V#30 = Utf8 Code#31 = Utf8 LineNumberTable#32 = Utf8 (Z)V#33 = Utf8 methodPrivate#34 = Utf8 getNum#35 = Utf8 (I)I#36 = Utf8 shwoGender#37 = Utf8 ()C#38 = Utf8 shwoInfo#39 = Utf8 <clinit>#40 = Utf8 SourceFile#41 = Utf8 JavapTest.java#42 = NameAndType #28:#29 // "<init>":()V#43 = Utf8 java#44 = NameAndType #23:#24 // info:Ljava/lang/String;#45 = NameAndType #19:#20 // flag:Z#46 = NameAndType #17:#18 // num:I#47 = NameAndType #21:#22 // gender:C#48 = Class #59 // java/lang/System#49 = NameAndType #60:#61 // out:Ljava/io/PrintStream;#50 = Utf8 java/lang/StringBuilder#51 = NameAndType #62:#63 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;#52 = NameAndType #62:#64 // append:(I)Ljava/lang/StringBuilder;#53 = NameAndType #65:#66 // toString:()Ljava/lang/String;#54 = Class #67 // java/io/PrintStream#55 = NameAndType #68:#69 // println:(Ljava/lang/String;)V#56 = Utf8 www.atguigu.com#57 = Utf8 JavapTest#58 = Utf8 java/lang/Object#59 = Utf8 java/lang/System#60 = Utf8 out#61 = Utf8 Ljava/io/PrintStream;#62 = Utf8 append#63 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;#64 = Utf8 (I)Ljava/lang/StringBuilder;#65 = Utf8 toString#66 = Utf8 ()Ljava/lang/String;#67 = Utf8 java/io/PrintStream#68 = Utf8 println#69 = Utf8 (Ljava/lang/String;)V##########################字段表集合的信息########################################{private int num; //字段名descriptor: I //字段描述符:字段的类型flags: ACC_PRIVATE //字段的访问标识boolean flag;descriptor: Zflags:protected char gender;descriptor: Cflags: ACC_PROTECTEDpublic java.lang.String info;descriptor: Ljava/lang/String;flags: ACC_PUBLICpublic static final int COUNTS;descriptor: Iflags: ACC_PUBLIC, ACC_STATIC, ACC_FINALConstantValue: int 1 //常量字段的属性:ConstantValue##############################方法表集合的信息######################################public JavapTest(); //构造器1的信息descriptor: ()Vflags: ACC_PUBLICCode:stack=2, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: aload_05: ldc #2 // String java7: putfield #3 // Field info:Ljava/lang/String;10: returnLineNumberTable:line 16: 0line 13: 4line 16: 10private JavapTest(boolean); //构造器2的信息descriptor: (Z)Vflags: ACC_PRIVATECode:stack=2, locals=2, args_size=20: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: aload_05: ldc #2 // String java7: putfield #3 // Field info:Ljava/lang/String;10: aload_011: iload_112: putfield #4 // Field flag:Z15: returnLineNumberTable:line 18: 0line 13: 4line 19: 10line 20: 15private void methodPrivate();descriptor: ()Vflags: ACC_PRIVATECode:stack=0, locals=1, args_size=10: returnLineNumberTable:line 23: 0int getNum(int);descriptor: (I)Iflags:Code:stack=2, locals=2, args_size=20: aload_01: getfield #5 // Field num:I4: iload_15: iadd6: ireturnLineNumberTable:line 25: 0protected char shwoGender();descriptor: ()Cflags: ACC_PROTECTEDCode:stack=1, locals=1, args_size=10: aload_01: getfield #6 // Field gender:C4: ireturnLineNumberTable:line 28: 0public void shwoInfo();descriptor: ()V //方法描述符:方法的形参列表、返回值类型flags: ACC_PUBLIC //方法的访问标识Code: //方法的Code属性stack=3, locals=2, args_size=1 //stack:操作数栈的最大深度,locals:局部变量表的长度,args_size:方法接收参数的个数//偏移量 操作码 操作数 //操作码占一个字节,操作数占两个字节0: bipush 102: istore_13: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;6: new #8 // class java/lang/StringBuilder9: dup10: invokespecial #9 // Method java/lang/StringBuilder."<init>":()V13: aload_014: getfield #3 // Field info:Ljava/lang/String;17: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;20: iload_121: invokevirtual #11 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;24: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;27: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V30: return//行号表:指明字节码指令的偏移量与java源程序中代码的行号的一一对应关系LineNumberTable:line 31: 0line 32: 3line 33: 30LocalVariableTable:Start Length Slot Name Signature0 31 0 this JavapTest;3 28 1 i Istatic {};descriptor: ()Vflags: ACC_STATICCode:stack=1, locals=1, args_size=00: ldc #14 // String www.atguigu.com2: astore_03: returnLineNumberTable:line 10: 0line 11: 3}SourceFile: "JavapTest.java" //附加属性:指明当前字节码文件对应的源程序文件名
在java文件所在目录用cmd执行 javap -v -p JavaTest.class>javaptest.txt,将反编译的字节码文件放入到javaptest.txt文件中



args_size:方法接收参数的个数

5、总结
1、通过javap命令可以查看一个java类反汇编得到的Class文件版本号、常量池、访问标识、变量表、指令代码行号表等等信息。不显示类索引、父类索引、接口索引集合、
2、通过对前面例子代码反汇编文件的简单分析,可以发现,一个方法的执行通常会涉及下面几块内存的操作:
(1) java栈中:局部变量表、操作数栈。
(2) java堆。通过对象的地址引用去操作。
(3)常量池。
(4)其他如帧数据区、方法区的剩余部分等情况,测试中没有显示出来,这里说明一下。
3、平常,我们比较关注的是java类中每个方法的反汇编中的指令操作过程,这些指令都是顺序执行的,可以参考官方文档查看每个指令的含义,很简单:
https : //docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html
