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

- 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 falg) {
- this.flag = flag;
- }
- private void methodPrivate() {
-
- }
- int getNum(int i) {
- return num + i;
- }
- protected char showGender() {
- return gender;
- }
- public void showInfo() {
- int i = 100;
- System.out.println(info + i);
- }
- }
javac -g JavapTest.java
javap -v -p JavapTest.class
- Classfile /D:/test/JavapTest.class // 字节码文件所属的路径
- Last modified 2022-9-9; size 1332 bytes // 最后修改时间,字节码文件的大小
- MD5 checksum b51bfb9f03b6513a89bc45cd775d7d35 // MD5散列值
- Compiled from "JavapTest.java" // 源文件的名称
- public class JavapTest
- minor version: 0 // 副版本
- major version: 52 // 主版本
- flags: ACC_PUBLIC, ACC_SUPER // 访问标识
- /*************************** 常量池********************************/
- Constant pool:
- #1 = Methodref #16.#47 // java/lang/Object."
" :()V - #2 = String #48 // java
- #3 = Fieldref #15.#49 // JavapTest.info:Ljava/lang/String;
- #4 = Fieldref #15.#50 // JavapTest.flag:Z
- #5 = Fieldref #15.#51 // JavapTest.num:I
- #6 = Fieldref #15.#52 // JavapTest.gender:C
- #7 = Fieldref #53.#54 // java/lang/System.out:Ljava/io/PrintStream;
- #8 = Class #55 // java/lang/StringBuilder
- #9 = Methodref #8.#47 // java/lang/StringBuilder."
" :()V - #10 = Methodref #8.#56 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
- #11 = Methodref #8.#57 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
- #12 = Methodref #8.#58 // java/lang/StringBuilder.toString:()Ljava/lang/String;
- #13 = Methodref #59.#60 // java/io/PrintStream.println:(Ljava/lang/String;)V
- #14 = String #61 // www.atguigu.com
- #15 = Class #62 // JavapTest
- #16 = Class #63 // 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 LocalVariableTable
- #33 = Utf8 this
- #34 = Utf8 LJavapTest;
- #35 = Utf8 (Z)V
- #36 = Utf8 falg
- #37 = Utf8 methodPrivate
- #38 = Utf8 getNum
- #39 = Utf8 (I)I
- #40 = Utf8 i
- #41 = Utf8 showGender
- #42 = Utf8 ()C
- #43 = Utf8 showInfo
- #44 = Utf8 <clinit>
- #45 = Utf8 SourceFile
- #46 = Utf8 JavapTest.java
- #47 = NameAndType #28:#29 // "
" :()V - #48 = Utf8 java
- #49 = NameAndType #23:#24 // info:Ljava/lang/String;
- #50 = NameAndType #19:#20 // flag:Z
- #51 = NameAndType #17:#18 // num:I
- #52 = NameAndType #21:#22 // gender:C
- #53 = Class #64 // java/lang/System
- #54 = NameAndType #65:#66 // out:Ljava/io/PrintStream;
- #55 = Utf8 java/lang/StringBuilder
- #56 = NameAndType #67:#68 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
- #57 = NameAndType #67:#69 // append:(I)Ljava/lang/StringBuilder;
- #58 = NameAndType #70:#71 // toString:()Ljava/lang/String;
- #59 = Class #72 // java/io/PrintStream
- #60 = NameAndType #73:#74 // println:(Ljava/lang/String;)V
- #61 = Utf8 www.atguigu.com
- #62 = Utf8 JavapTest
- #63 = Utf8 java/lang/Object
- #64 = Utf8 java/lang/System
- #65 = Utf8 out
- #66 = Utf8 Ljava/io/PrintStream;
- #67 = Utf8 append
- #68 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
- #69 = Utf8 (I)Ljava/lang/StringBuilder;
- #70 = Utf8 toString
- #71 = Utf8 ()Ljava/lang/String;
- #72 = Utf8 java/io/PrintStream
- #73 = Utf8 println
- #74 = Utf8 (Ljava/lang/String;)V
- /******************************字段表集合的信息**************************************/
- {
- private int num; // 字段名
- descriptor: I // 字段表集合的信息
- flags: ACC_PRIVATE // 字段的访问标识
-
- boolean flag;
- descriptor: Z
- flags:
-
- protected char gender;
- descriptor: C
- flags: ACC_PROTECTED
-
- public java.lang.String info;
- descriptor: Ljava/lang/String;
- flags: ACC_PUBLIC
-
- public static final int COUNTS;
- descriptor: I
- flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
- ConstantValue: int 1 // 常量字段的属性:ConstantValue
- /******************************方法表集合的信息**************************************/
- public JavapTest(); // 无参构造器方法信息
- descriptor: ()V
- flags: ACC_PUBLIC
- Code:
- stack=2, locals=1, args_size=1
- 0: aload_0
- 1: invokespecial #1 // Method java/lang/Object."
" :()V - 4: aload_0
- 5: ldc #2 // String java
- 7: putfield #3 // Field info:Ljava/lang/String;
- 10: return
- LineNumberTable:
- line 14: 0
- line 12: 4
- line 16: 10
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 11 0 this LJavapTest;
-
- private JavapTest(boolean); // 单个参数构造器方法信息
- descriptor: (Z)V
- flags: ACC_PRIVATE
- Code:
- stack=2, locals=2, args_size=2
- 0: aload_0
- 1: invokespecial #1 // Method java/lang/Object."
" :()V - 4: aload_0
- 5: ldc #2 // String java
- 7: putfield #3 // Field info:Ljava/lang/String;
- 10: aload_0
- 11: aload_0
- 12: getfield #4 // Field flag:Z
- 15: putfield #4 // Field flag:Z
- 18: return
- LineNumberTable:
- line 17: 0
- line 12: 4
- line 18: 10
- line 19: 18
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 19 0 this LJavapTest;
- 0 19 1 falg Z
-
- private void methodPrivate();
- descriptor: ()V
- flags: ACC_PRIVATE
- Code:
- stack=0, locals=1, args_size=1
- 0: return
- LineNumberTable:
- line 22: 0
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 1 0 this LJavapTest;
-
- int getNum(int);
- descriptor: (I)I
- flags:
- Code:
- stack=2, locals=2, args_size=2
- 0: aload_0
- 1: getfield #5 // Field num:I
- 4: iload_1
- 5: iadd
- 6: ireturn
- LineNumberTable:
- line 24: 0
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 7 0 this LJavapTest;
- 0 7 1 i I
-
- protected char showGender();
- descriptor: ()C
- flags: ACC_PROTECTED
- Code:
- stack=1, locals=1, args_size=1
- 0: aload_0
- 1: getfield #6 // Field gender:C
- 4: ireturn
- LineNumberTable:
- line 27: 0
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 5 0 this LJavapTest;
-
- public void showInfo(); // 方法的描述符:方法的形参列表、返回值类型
- descriptor: ()V // 方法的访问标识
- flags: ACC_PUBLIC // 方法的Code属性
- Code: // stack:操作数栈的最大深度 locals:局部变量表的长度 args_size:方法接受参数的个数
- stack=3, locals=2, args_size=1
- // 偏移量 操作码 操作数
- 0: bipush 100
- 2: istore_1
- 3: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
- 6: new #8 // class java/lang/StringBuilder
- 9: dup
- 10: invokespecial #9 // Method java/lang/StringBuilder."
" :()V - 13: aload_0
- 14: getfield #3 // Field info:Ljava/lang/String;
- 17: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
- 20: iload_1
- 21: 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;)V
- 30: return
- // 行号表:指明字节码指令的偏移量与java源代码中代码的行号的一一对应关系
- LineNumberTable:
- line 30: 0
- line 31: 3
- line 32: 30
- // 局部变量表:描述内部局部变量的相关信息
- LocalVariableTable:
- Start Length Slot Name Signature
- 0 31 0 this LJavapTest;
- 3 28 1 i I
-
- static {};
- descriptor: ()V
- flags: ACC_STATIC
- Code:
- stack=1, locals=1, args_size=0
- 0: ldc #14 // String www.atguigu.com
- 2: astore_0
- 3: return
- LineNumberTable:
- line 9: 0
- line 10: 3
- LocalVariableTable:
- Start Length Slot Name Signature
- }
- SourceFile: "JavapTest.java"// 附加属性:指明当前字节码文件对应的源程序文件名
jclasslib展示的内容:

1、通过javap命令可以查看一个java 类反汇编得到的Class文件版本号、常量池、访问标识、 变量表、指令代码行号表等信息。不显示类索引、父类索引、接口索引集合、
2、通过对前面例子代码反汇编文件的简单分析,可以发现,一个方法的执行通常会涉及下面几块内存的操作
3、平常,我们比较关注的是java类中每个方法的反汇编中的指令操作过程,这些指令都是顺序执行的,可以参考官方文档查看每个指令的含义,很简单:
Chapter 6. The Java Virtual Machine Instruction Set (oracle.com)