Java-工具javap讲解

  javap,是Java class文件分解器,反编译class文件,也可以查看java编译器生成的字节码。主要用于分解class文件。学习这个工具之前可以先去简单了解一下class类文件结构

基本语法

命令格式:

1
javap [option] class文件(没有后缀名)

如果[option]为空,javap将输出传递给它的类的public域及方法,并输出到标准输出设备上(默认I/O为控制台)。如下代码,先javac编译源码,然后javap反编译class文件,注意不需要添加“.class”后缀。源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.xxo.demo.util;

/**
* Created by xiaoxiaomo on 2016/3/31.
*/
public class JavapDemo {
private int num = 10 ;
public static String name = "momo" ;

public static void main(String[] args) {
String a = name ;
String b = "abc" ;
String c = a+b ;
System.out.println(c);
}
}

  • 下面来看一下javap的一些选项(常用的)
1
2
3
4
5
6
7
8
9
javap -help #输出 javap 的帮助信息。
javap  -l #输出行及局部变量表。
javap -public #只显示public类及成员。
javap -protected #只显示protected和public类及成员。
javap -package #只显示包、protected和public类及成员。这是缺省设置。
javap -private #显示所有类和成员。
javap -s 输出内部类型签名。
javap -c #输出类中各方法的未解析的代码,即构成Java字节码的指令。
javap -verbose #输出堆栈大小、各方法的locals及args数,以及class文件的编译版本

javap -l 命令

public com.xxo.demo.util.JavapDemo(); 表示的默认的无参构造函数,

javap -pub|pro|pac|pri

  • 来简单看一下javap -public

就会显示出类中所用的public的类,构造方法,全局变量,局部变量就不会在此显示:

1
2
3
4
5
public class com.xxo.demo.util.JavapDemo extends java.lang.Object{//类信息
public static java.lang.String name;//public全部变量
public com.xxo.demo.util.JavapDemo();//默认的构造方法
public static void main(java.lang.String[]);//main方法
}

下面来修改一下源代码,

  1. 私有化一下默认构造方法,然后重写一下带参构造方法
  2. 定义一个protected的全局变量
  3. private的HashMap类型的变量
  4. 修改后javac编译源码

源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.xxo.demo.util;

import java.util.HashMap;

/**
* javap Demo
* Created by xiaoxiaomo on 2016/3/31.
*/
public class JavapDemo {
private int num = 10 ;//私有全局变量
public static String name = "momo" ;//共有全局变量
protected Double height = 175.0 ;//protected
private HashMap map ;

private JavapDemo() {}
public JavapDemo(int num) {
this.num = num;
}

public static void main(String[] args) {
String a = name ;
String b = "abc" ;
String c = a+b ;
System.out.println(c);
}
}
  • 使用javap -protected查看,显示protected和public类及成员:

  • 使用javap -package查看,会显示包、protected和public类及成员:

  • 使用javap -private查看,会显示所用成员:

javap -c 命令

javap -c #输出类中各方法的未解析的代码,即构成Java字节码的指令。我们这里还是使用上例中的源码,输入命令:

1
$  javap -c JavapDemo
  • 编译后结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Compiled from "JavapDemo.java"                                                             
public class com.xxo.demo.util.JavapDemo extends java.lang.Object{
public static java.lang.String name; //静态的全局变量name

protected java.lang.Double height;//Double类型的变量height

public com.xxo.demo.util.JavapDemo(int);//带参构造函数
Code:
0: aload_0 //将引用类型的局部变量加载到操作栈,即this
1: invokespecial #1; //Method java/lang/Object."<init>":()V //实例化对象this
4: aload_0 //将引用类型的局部变量加载到操作栈,即this
5: bipush 10 //将一个常量加载到操作数栈,10
7: putfield #2; //Field num:I //设置类中字段this.num的值为10
10: aload_0 //加载局部变量表中的数据到操作栈中,即this
11: ldc2_w #3; //double 175.0d //将double类型的常量175.0d加载到操作数栈
14: invokestatic #5; //Method java/lang/Double.valueOf:(D)Ljava/lang/Double;//调Double的valueOf方法
17: putfield #6; //Field height:Ljava/lang/Double;//设置类中字段this.height的值
20: aload_0 //加载局部变量表中的数据到操作栈中,即this
21: iload_1 // 将int类型的局部变量加载到操作栈,即方法传递的num
22: putfield #2; //Field num:I //设置类中字段this.num的值为num
25: return //没有返回值

public static void main(java.lang.String[]); //main方法
Code:
0: getstatic #7; //Field name:Ljava/lang/String; //获取静态变量name,并压入栈顶
3: astore_1 //将变量从操作数栈存储到局部变量中,(弹栈),即存入a中
4: ldc #8; //String abc //字符串常量加载到操作数栈,abc
6: astore_2 //将abc存储到局部变量表中,b中
7: new #9; //class java/lang/StringBuilder //创建一个StringBuilder对象
10: dup //直接操作操作数栈
11: invokespecial #10; //Method java/lang/StringBuilder."<init>":()V //
14: aload_1 //加载局部变量表中的数据到操作栈中,即a的数据(第四条指令把a已加载到局部变量表中)
15: invokevirtual #11; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
//调用Builder的append方法
18: aload_2 //加载局部变量表中的数据到操作栈中,即b的数据
19: invokevirtual #11; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
//调用Builder的append方法
22: invokevirtual #12; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;//调用Builder的toString方法

25: astore_3 //将a+b的结果存储到局部变量表中,即c中
26: getstatic #13; //Field java/lang/System.out:Ljava/io/PrintStream;//获取输出流
29: aload_3 //加载局部变量表中的数据到操作栈中(第25条指令把c加载到了局部变量表)
30: invokevirtual #14; //Method java/io/PrintStream.println:(Ljava/lang/String;)V //并输出打印
33: return //没有返回值

static {};
Code:
0: ldc #15; //String momo //字符串常量加载到操作数栈,"momo"
2: putstatic #7; //Field name:Ljava/lang/String; //设置类中字段name的值为momo
5: return //没有返回值

}

javap -verbose

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139

Compiled from "JavapDemo.java"
public class com.xxo.demo.util.JavapDemo extends java.lang.Object
SourceFile: "JavapDemo.java"
minor version: 0
major version: 50
Constant pool:
const #1 = Method #17.#36; // java/lang/Object."<init>":()V
const #2 = Field #16.#37; // com/xxo/demo/util/JavapDemo.num:I
const #3 = double 175.0d;
const #5 = Method #38.#39; // java/lang/Double.valueOf:(D)Ljava/lang/Double; const #6 = Field #16.#40; // com/xxo/demo/util/JavapDemo.height:Ljava/lang/Double;
const #7 = Field #16.#41; // com/xxo/demo/util/JavapDemo.name:Ljava/lang/String;
const #8 = String #42; // abc
const #9 = class #43; // java/lang/StringBuilder
const #10 = Method #9.#36; // java/lang/StringBuilder."<init>":()V
const #11 = Method #9.#44; // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
const #12 = Method #9.#45; // java/lang/StringBuilder.toString:()Ljava/lang/String;
const #13 = Field #46.#47; // java/lang/System.out:Ljava/io/PrintStream;
const #14 = Method #48.#49; // java/io/PrintStream.println:(Ljava/lang/String;)V
const #15 = String #50; // momo
const #16 = class #51; // com/xxo/demo/util/JavapDemo
const #17 = class #52; // java/lang/Object
const #18 = Asciz num;
const #19 = Asciz I;
const #20 = Asciz name;
const #21 = Asciz Ljava/lang/String;;
const #22 = Asciz height;
const #23 = Asciz Ljava/lang/Double;;
const #24 = Asciz map;
const #25 = Asciz Ljava/util/HashMap;;
const #26 = Asciz <init>;
const #27 = Asciz ()V;
const #28 = Asciz Code;
const #29 = Asciz LineNumberTable;
const #30 = Asciz (I)V;
const #31 = Asciz main;
const #32 = Asciz ([Ljava/lang/String;)V;
const #33 = Asciz <clinit>;
const #34 = Asciz SourceFile;
const #35 = Asciz JavapDemo.java;
const #36 = NameAndType #26:#27;// "<init>":()V
const #37 = NameAndType #18:#19;// num:I
const #38 = class #53; // java/lang/Double
const #39 = NameAndType #54:#55;// valueOf:(D)Ljava/lang/Double;
const #40 = NameAndType #22:#23;// height:Ljava/lang/Double;
const #41 = NameAndType #20:#21;// name:Ljava/lang/String;
const #42 = Asciz abc;
const #43 = Asciz java/lang/StringBuilder;
const #44 = NameAndType #56:#57;// append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
const #45 = NameAndType #58:#59;// toString:()Ljava/lang/String;
const #46 = class #60; // java/lang/System
const #47 = NameAndType #61:#62;// out:Ljava/io/PrintStream;
const #48 = class #63; // java/io/PrintStream
const #49 = NameAndType #64:#65;// println:(Ljava/lang/String;)V
const #50 = Asciz momo;
const #51 = Asciz com/xxo/demo/util/JavapDemo;
const #52 = Asciz java/lang/Object;
const #53 = Asciz java/lang/Double;
const #54 = Asciz valueOf;
const #55 = Asciz (D)Ljava/lang/Double;;
const #56 = Asciz append;
const #57 = Asciz (Ljava/lang/String;)Ljava/lang/StringBuilder;;
const #58 = Asciz toString;
const #59 = Asciz ()Ljava/lang/String;;
const #60 = Asciz java/lang/System;
const #61 = Asciz out;
const #62 = Asciz Ljava/io/PrintStream;;
const #63 = Asciz java/io/PrintStream;
const #64 = Asciz println;
const #65 = Asciz (Ljava/lang/String;)V;

{
public static java.lang.String name;

protected java.lang.Double height;

public com/xxo.demo.util.JavapDemo(int);
Code:
Stack=3, Locals=2, Args_size=2
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 10
7: putfield #2; //Field num:I
10: aload_0
11: ldc2_w #3; //double 175.0d
14: invokestatic #5; //Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
17: putfield #6; //Field height:Ljava/lang/Double;
20: aload_0
21: iload_1
22: putfield #2; //Field num:I
25: return
LineNumberTable:
line 16: 0
line 10: 4
line 12: 10
line 17: 20
line 18: 25


public static void main(java.lang.String[]);
Code:
Stack=2, Locals=4, Args_size=1
0: getstatic #7; //Field name:Ljava/lang/String;
3: astore_1
4: ldc #8; //String abc
6: astore_2
7: new #9; //class java/lang/StringBuilder
10: dup
11: invokespecial #10; //Method java/lang/StringBuilder."<init>":()V
14: aload_1
15: invokevirtual #11; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
18: aload_2
19: invokevirtual #11; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: invokevirtual #12; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;

25: astore_3
26: getstatic #13; //Field java/lang/System.out:Ljava/io/PrintStream;
29: aload_3
30: invokevirtual #14; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
33: return
LineNumberTable:
line 21: 0
line 22: 4
line 23: 7
line 24: 26
line 25: 33


static {};
Code:
Stack=1, Locals=0, Args_size=0 //
0: ldc #15; //String momo
2: putstatic #7; //Field name:Ljava/lang/String;
5: return
LineNumberTable:
line 11: 0

}

本篇博客就不一一讲解字节码指令的具体含义了,在后面的一篇博客”java-字节码“,会详细的讲解字节码指令的具体含义。

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器