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有如下选项:
    image.png
    image.png
    image.png

4、使用举例

  1. public class JavapTest{
  2. private int num;
  3. boolean flag;
  4. protected char gender;
  5. public String info;
  6. public static final int COUNTS = 1;
  7. static{
  8. String url = "www.atguigu.com";
  9. }
  10. {
  11. info = "java";
  12. }
  13. public JavapTest(){}
  14. private JavapTest(boolean flag){
  15. this.flag = flag;
  16. }
  17. private void methodPrivate(){
  18. }
  19. int getNum(int i){
  20. return num + i;
  21. }
  22. protected char shwoGender(){
  23. return gender;
  24. }
  25. public void shwoInfo(){
  26. int i = 10;
  27. System.out.println(info + i);
  28. }
  29. }
  1. Classfile /F:/home/idea/JavapTest.class //字节码文件所属路径
  2. Last modified 2021-4-26; size 1130 bytes //最后修改时间,字节码文件的大小
  3. MD5 checksum 206819f6dea876b5ebbd2afa2aaa3fe1 //MD5散列值
  4. Compiled from "JavapTest.java" //源文件的名称
  5. public class JavapTest
  6. minor version: 0 //副版本
  7. major version: 52 //主版本
  8. flags: ACC_PUBLIC, ACC_SUPER //访问标识
  9. Constant pool: //常量池
  10. #1 = Methodref #16.#42 // java/lang/Object."<init>":()V
  11. #2 = String #43 // java
  12. #3 = Fieldref #15.#44 // JavapTest.info:Ljava/lang/String;
  13. #4 = Fieldref #15.#45 // JavapTest.flag:Z
  14. #5 = Fieldref #15.#46 // JavapTest.num:I
  15. #6 = Fieldref #15.#47 // JavapTest.gender:C
  16. #7 = Fieldref #48.#49 // java/lang/System.out:Ljava/io/PrintStream;
  17. #8 = Class #50 // java/lang/StringBuilder
  18. #9 = Methodref #8.#42 // java/lang/StringBuilder."<init>":()V
  19. #10 = Methodref #8.#51 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  20. #11 = Methodref #8.#52 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  21. #12 = Methodref #8.#53 // java/lang/StringBuilder.toString:()Ljava/lang/String;
  22. #13 = Methodref #54.#55 // java/io/PrintStream.println:(Ljava/lang/String;)V
  23. #14 = String #56 // www.atguigu.com
  24. #15 = Class #57 // JavapTest
  25. #16 = Class #58 // java/lang/Object
  26. #17 = Utf8 num
  27. #18 = Utf8 I
  28. #19 = Utf8 flag
  29. #20 = Utf8 Z
  30. #21 = Utf8 gender
  31. #22 = Utf8 C
  32. #23 = Utf8 info
  33. #24 = Utf8 Ljava/lang/String;
  34. #25 = Utf8 COUNTS
  35. #26 = Utf8 ConstantValue
  36. #27 = Integer 1
  37. #28 = Utf8 <init>
  38. #29 = Utf8 ()V
  39. #30 = Utf8 Code
  40. #31 = Utf8 LineNumberTable
  41. #32 = Utf8 (Z)V
  42. #33 = Utf8 methodPrivate
  43. #34 = Utf8 getNum
  44. #35 = Utf8 (I)I
  45. #36 = Utf8 shwoGender
  46. #37 = Utf8 ()C
  47. #38 = Utf8 shwoInfo
  48. #39 = Utf8 <clinit>
  49. #40 = Utf8 SourceFile
  50. #41 = Utf8 JavapTest.java
  51. #42 = NameAndType #28:#29 // "<init>":()V
  52. #43 = Utf8 java
  53. #44 = NameAndType #23:#24 // info:Ljava/lang/String;
  54. #45 = NameAndType #19:#20 // flag:Z
  55. #46 = NameAndType #17:#18 // num:I
  56. #47 = NameAndType #21:#22 // gender:C
  57. #48 = Class #59 // java/lang/System
  58. #49 = NameAndType #60:#61 // out:Ljava/io/PrintStream;
  59. #50 = Utf8 java/lang/StringBuilder
  60. #51 = NameAndType #62:#63 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  61. #52 = NameAndType #62:#64 // append:(I)Ljava/lang/StringBuilder;
  62. #53 = NameAndType #65:#66 // toString:()Ljava/lang/String;
  63. #54 = Class #67 // java/io/PrintStream
  64. #55 = NameAndType #68:#69 // println:(Ljava/lang/String;)V
  65. #56 = Utf8 www.atguigu.com
  66. #57 = Utf8 JavapTest
  67. #58 = Utf8 java/lang/Object
  68. #59 = Utf8 java/lang/System
  69. #60 = Utf8 out
  70. #61 = Utf8 Ljava/io/PrintStream;
  71. #62 = Utf8 append
  72. #63 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
  73. #64 = Utf8 (I)Ljava/lang/StringBuilder;
  74. #65 = Utf8 toString
  75. #66 = Utf8 ()Ljava/lang/String;
  76. #67 = Utf8 java/io/PrintStream
  77. #68 = Utf8 println
  78. #69 = Utf8 (Ljava/lang/String;)V
  79. ##########################字段表集合的信息########################################
  80. {
  81. private int num; //字段名
  82. descriptor: I //字段描述符:字段的类型
  83. flags: ACC_PRIVATE //字段的访问标识
  84. boolean flag;
  85. descriptor: Z
  86. flags:
  87. protected char gender;
  88. descriptor: C
  89. flags: ACC_PROTECTED
  90. public java.lang.String info;
  91. descriptor: Ljava/lang/String;
  92. flags: ACC_PUBLIC
  93. public static final int COUNTS;
  94. descriptor: I
  95. flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
  96. ConstantValue: int 1 //常量字段的属性:ConstantValue
  97. ##############################方法表集合的信息######################################
  98. public JavapTest(); //构造器1的信息
  99. descriptor: ()V
  100. flags: ACC_PUBLIC
  101. Code:
  102. stack=2, locals=1, args_size=1
  103. 0: aload_0
  104. 1: invokespecial #1 // Method java/lang/Object."<init>":()V
  105. 4: aload_0
  106. 5: ldc #2 // String java
  107. 7: putfield #3 // Field info:Ljava/lang/String;
  108. 10: return
  109. LineNumberTable:
  110. line 16: 0
  111. line 13: 4
  112. line 16: 10
  113. private JavapTest(boolean); //构造器2的信息
  114. descriptor: (Z)V
  115. flags: ACC_PRIVATE
  116. Code:
  117. stack=2, locals=2, args_size=2
  118. 0: aload_0
  119. 1: invokespecial #1 // Method java/lang/Object."<init>":()V
  120. 4: aload_0
  121. 5: ldc #2 // String java
  122. 7: putfield #3 // Field info:Ljava/lang/String;
  123. 10: aload_0
  124. 11: iload_1
  125. 12: putfield #4 // Field flag:Z
  126. 15: return
  127. LineNumberTable:
  128. line 18: 0
  129. line 13: 4
  130. line 19: 10
  131. line 20: 15
  132. private void methodPrivate();
  133. descriptor: ()V
  134. flags: ACC_PRIVATE
  135. Code:
  136. stack=0, locals=1, args_size=1
  137. 0: return
  138. LineNumberTable:
  139. line 23: 0
  140. int getNum(int);
  141. descriptor: (I)I
  142. flags:
  143. Code:
  144. stack=2, locals=2, args_size=2
  145. 0: aload_0
  146. 1: getfield #5 // Field num:I
  147. 4: iload_1
  148. 5: iadd
  149. 6: ireturn
  150. LineNumberTable:
  151. line 25: 0
  152. protected char shwoGender();
  153. descriptor: ()C
  154. flags: ACC_PROTECTED
  155. Code:
  156. stack=1, locals=1, args_size=1
  157. 0: aload_0
  158. 1: getfield #6 // Field gender:C
  159. 4: ireturn
  160. LineNumberTable:
  161. line 28: 0
  162. public void shwoInfo();
  163. descriptor: ()V //方法描述符:方法的形参列表、返回值类型
  164. flags: ACC_PUBLIC //方法的访问标识
  165. Code: //方法的Code属性
  166. stack=3, locals=2, args_size=1 //stack:操作数栈的最大深度,locals:局部变量表的长度,args_size:方法接收参数的个数
  167. //偏移量 操作码 操作数 //操作码占一个字节,操作数占两个字节
  168. 0: bipush 10
  169. 2: istore_1
  170. 3: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
  171. 6: new #8 // class java/lang/StringBuilder
  172. 9: dup
  173. 10: invokespecial #9 // Method java/lang/StringBuilder."<init>":()V
  174. 13: aload_0
  175. 14: getfield #3 // Field info:Ljava/lang/String;
  176. 17: invokevirtual #10 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  177. 20: iload_1
  178. 21: invokevirtual #11 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  179. 24: invokevirtual #12 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  180. 27: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
  181. 30: return
  182. //行号表:指明字节码指令的偏移量与java源程序中代码的行号的一一对应关系
  183. LineNumberTable:
  184. line 31: 0
  185. line 32: 3
  186. line 33: 30
  187. LocalVariableTable:
  188. Start Length Slot Name Signature
  189. 0 31 0 this JavapTest;
  190. 3 28 1 i I
  191. static {};
  192. descriptor: ()V
  193. flags: ACC_STATIC
  194. Code:
  195. stack=1, locals=1, args_size=0
  196. 0: ldc #14 // String www.atguigu.com
  197. 2: astore_0
  198. 3: return
  199. LineNumberTable:
  200. line 10: 0
  201. line 11: 3
  202. }
  203. SourceFile: "JavapTest.java" //附加属性:指明当前字节码文件对应的源程序文件名

在java文件所在目录用cmd执行 javap -v -p JavaTest.class>javaptest.txt,将反编译的字节码文件放入到javaptest.txt文件中
image.png
image.png
image.png
image.png
args_size:方法接收参数的个数
image.png
image.png
image.png

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