javap
将一个或多个类文件进行分解。
使用简要说明
javap [options] classfile...
options
命令行选项,详细查看后面的Options介绍
classfile
一个或多个Class文件(多个使用空格分离),可以使用文件路径或者classPath下的文件或者输入URL
Description
javap命令分解卸一个或多个类文件。输出取决于所使用的选项。当没有使用任何选项,那么javap命令打印方案为protected和公共字段和方法。javap命令将输出打印到控制台。
Options
-help
--help
-?
打印帮助信息
-version
打印版本信息
-l
打印行内变量以及局部变量
-public
显示public访问修饰的内容
-protected
显示protected、public访问修饰的内容
-private
-p
显示所有的内容
-Joption
将指定的选项传递给JVM。例如:
javap -J-version
javap -J-Djava.security.manager -J-Djava.security.policy=MyPolicy MyClassName
For more information about JVM options, see the java command documentation.
-s
打印内部类签名
-sysinfo
显示系统信息(路径、大小、日期、MD5哈希)的类处理。
-constants
显示静态常量
-c
为每个类中的方法打印反汇编代码,例如,Java字节码指令组成,。
-verbose
打印堆栈大小,局部变量的数目和方法的参数。
Prints stack size, number of locals and arguments for methods.
-classpath path
指定javap命令使用的路径查找类。覆盖默认的或者当它被设置CLASSPATH环境变量。
Specifies the path the javap command uses to look up classes. Overrides the default or the CLASSPATH environment variable when it is set.
-bootclasspath path
指定的路径加载引导类。默认情况下,引导类类,实现核心Java平台位于jre / lib / rt。jar和其他几个jar文件。
Specifies the path from which to load bootstrap classes. By default, the bootstrap classes are the classes that implement the core Java platform located in jre/lib/rt.jar and several other JAR files.
-extdir dirs
增加一些扩展路径用以获取类库
Overrides the location at which installed extensions are searched for. The default location for extensions is the value of java.ext.dirs.
###我们来看看String、StringBuffer、StringBuilder的不同 ####测试类
public class Test {
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
//我们一般拼接字符的时候都不会拼接太多次100次其实就算比较多了
contactStringWithLoop(100);
}
System.out.println(System.currentTimeMillis() - start);
//916毫秒
start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
//我们一般拼接字符的时候都不会拼接太多次100次其实就算比较多了
contactStringWithStringBuilder(100);
}
System.out.println(System.currentTimeMillis() - start);
//244毫秒
start = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
//我们一般拼接字符的时候都不会拼接太多次100次其实就算比较多了
contactStringWithStringBuffer(100);
}
System.out.println(System.currentTimeMillis() - start);
//620毫秒
}
/**
* 直接拼接字符串
*
* @return
*/
public static String contactString() {
String string = "直接" + "对字符串" + "进行" + "多次的拼接"
+ "看看最后编译" + "的字节码" + "会是神马" + "样子" + "的";
return string;
}
/**
* 与上面的方法其实一致,只是在拼接中引入了一个多个变量
*
* @param str
* @return
*/
public static String contactStringWithParam(String str, String str2, String str3) {
String string = "直接" + str3 + "进行" + "多次的拼接"
+ "看看最后编译" + str + "会是神马" + str2 + "的";
return string;
}
/**
* 通过循环来拼接字符串
*
* @param loopCount 循环的次数
* @return
*/
public static String contactStringWithLoop(int loopCount) {
String string = "";
for (int i = 0; i < loopCount; i++) {
string += i;
}
return string;
}
/**
* 使用StringBuffer循环拼接字符串
*
* @param loopCount
* @return
*/
public static String contactStringWithStringBuffer(int loopCount) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < loopCount; i++) {
sb.append(i);
}
return sb.toString();
}
/**
* 使用StringBuilder循环拼接字符串
*
* @param loopCount
* @return
*/
public static String contactStringWithStringBuilder(int loopCount) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < loopCount; i++) {
sb.append(i);
}
return sb.toString();
}
}
####使用javap -c Test 来获得字节码信息
Compiled from "Test.java"
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
3: lstore_1
4: iconst_0
5: istore_3
6: iload_3
7: ldc #3 // int 100000
9: if_icmpge 24
12: bipush 100
14: invokestatic #4 // Method contactStringWithLoop:(I)Ljava/lang/String;
17: pop
18: iinc 3, 1
21: goto 6
24: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
27: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
30: lload_1
31: lsub
32: invokevirtual #6 // Method java/io/PrintStream.println:(J)V
35: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
38: lstore_1
39: iconst_0
40: istore_3
41: iload_3
42: ldc #3 // int 100000
44: if_icmpge 59
47: bipush 100
49: invokestatic #7 // Method contactStringWithStringBuilder:(I)Ljava/lang/String;
52: pop
53: iinc 3, 1
56: goto 41
59: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
62: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
65: lload_1
66: lsub
67: invokevirtual #6 // Method java/io/PrintStream.println:(J)V
70: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
73: lstore_1
74: iconst_0
75: istore_3
76: iload_3
77: ldc #3 // int 100000
79: if_icmpge 94
82: bipush 100
84: invokestatic #8 // Method contactStringWithStringBuffer:(I)Ljava/lang/String;
87: pop
88: iinc 3, 1
91: goto 76
94: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
97: invokestatic #2 // Method java/lang/System.currentTimeMillis:()J
100: lload_1
101: lsub
102: invokevirtual #6 // Method java/io/PrintStream.println:(J)V
105: return
public static java.lang.String contactString();
Code:
0: ldc #9 // String 直接对字符串进行多次的拼接看看最后编译的字节码会是神马样子的
2: astore_0
3: aload_0
4: areturn
public static java.lang.String contactStringWithParam(java.lang.String, java.lang.String, java.lang.String);
Code:
0: new #10 // class java/lang/StringBuilder
3: dup
4: invokespecial #11 // Method java/lang/StringBuilder."<init>":()V
7: ldc #12 // String 直接
9: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
12: aload_2
13: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
16: ldc #14 // String 进行
18: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: ldc #15 // String 多次的拼接
23: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
26: ldc #16 // String 看看最后编译
28: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31: aload_0
32: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
35: ldc #17 // String 会是神马
37: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
40: aload_1
41: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
44: ldc #18 // String 的
46: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
49: invokevirtual #19 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
52: astore_3
53: aload_3
54: areturn
public static java.lang.String contactStringWithLoop(int);
Code:
0: ldc #20 // String
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: iload_0
7: if_icmpge 35
10: new #10 // class java/lang/StringBuilder
13: dup
14: invokespecial #11 // Method java/lang/StringBuilder."<init>":()V
17: aload_1
18: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: iload_2
22: invokevirtual #21 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
25: invokevirtual #19 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
28: astore_1
29: iinc 2, 1
32: goto 5
35: aload_1
36: areturn
public static java.lang.String contactStringWithStringBuffer(int);
Code:
0: new #22 // class java/lang/StringBuffer
3: dup
4: invokespecial #23 // Method java/lang/StringBuffer."<init>":()V
7: astore_1
8: iconst_0
9: istore_2
10: iload_2
11: iload_0
12: if_icmpge 27
15: aload_1
16: iload_2
17: invokevirtual #24 // Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer;
20: pop
21: iinc 2, 1
24: goto 10
27: aload_1
28: invokevirtual #25 // Method java/lang/StringBuffer.toString:()Ljava/lang/String;
31: areturn
public static java.lang.String contactStringWithStringBuilder(int);
Code:
0: new #10 // class java/lang/StringBuilder
3: dup
4: invokespecial #11 // Method java/lang/StringBuilder."<init>":()V
7: astore_1
8: iconst_0
9: istore_2
10: iload_2
11: iload_0
12: if_icmpge 27
15: aload_1
16: iload_2
17: invokevirtual #21 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
20: pop
21: iinc 2, 1
24: goto 10
27: aload_1
28: invokevirtual #19 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: areturn
}
###我们来分一下分解出的字节码: #####contactString方法[在javac结果的64行]
public static java.lang.String contactString();
Code:
0: ldc #9 // String 直接对字符串进行多次的拼接看看最后编译的字节码会是神马样子的
2: astore_0
3: aload_0
4: areturn
从0: ldc #9 // String 直接对字符串进行多次的拼接看看最后编译的字节码会是神马样子的
这里可以看出,编译器直接将编译结果进行了转换,没有使用+而直接使用了拼接后的字符串(因为不包含变量的拼接,所以可以预想到最终结果) 结论:对java中字符串直接拼接时可以直接使用+的方式来拼接。 #####contactStringWithParam[在javac结果的71行]
public static java.lang.String contactStringWithParam(java.lang.String, java.lang.String, java.lang.String);
Code:
0: new #10 // class java/lang/StringBuilder
3: dup
4: invokespecial #11 // Method java/lang/StringBuilder."<init>":()V
7: ldc #12 // String 直接
9: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
12: aload_2
13: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
16: ldc #14 // String 进行
18: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: ldc #15 // String 多次的拼接
23: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
26: ldc #16 // String 看看最后编译
28: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31: aload_0
32: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
35: ldc #17 // String 会是神马
37: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
40: aload_1
41: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
44: ldc #18 // String 的
46: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
49: invokevirtual #19 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
52: astore_3
53: aload_3
54: areturn
0: new #10 // class java/lang/StringBuilder
这句话是对应声明了一个StringBuilder,然后每次拼接实际使用的是StringBuilder的append方法。 结论:在针对这种没有循环但是有变变量拼接的字符串时,使用StringBuilder与使用String + 的方式没有区别,但是String + 的方式更加省时省力,而且相对清晰。 #####contactStringWithLoop[在javac结果的99行]
public static java.lang.String contactStringWithLoop(int);
Code:
0: ldc #20 // String
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: iload_0
7: if_icmpge 35
10: new #10 // class java/lang/StringBuilder
13: dup
14: invokespecial #11 // Method java/lang/StringBuilder."<init>":()V
17: aload_1
18: invokevirtual #13 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: iload_2
22: invokevirtual #21 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
25: invokevirtual #19 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
28: astore_1
29: iinc 2, 1
32: goto 5
35: aload_1
36: areturn
首先先初始化循环变量:
0: iconst_0
1: istore_1
这两行代码相当于int i = 0
这句代码(iconst_0 是数字 0,istore_1 就是表示局部变量1,这里就是源码里的 i 了)
5: iload_2
6: iload_0
7: if_icmpge 35
这三行意思就是 i 是否小于 10 ,小于则继续往下执行,否则就跳到 编号为 35的 return那里也即跳出for循环了,所以这里的实际意义是每次循环时新建一个StringBuilder,然后本次循环结束时返回StringBuilder的toString()的结果。 #####对比contactStringWithStringBuffer[在javac结果的122行]
public static java.lang.String contactStringWithStringBuffer(int);
Code:
0: new #22 // class java/lang/StringBuffer
3: dup
4: invokespecial #23 // Method java/lang/StringBuffer."<init>":()V
7: astore_1
8: iconst_0
9: istore_2
10: iload_2
11: iload_0
12: if_icmpge 27
15: aload_1
16: iload_2
17: invokevirtual #24 // Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer;
20: pop
21: iinc 2, 1
24: goto 10
27: aload_1
28: invokevirtual #25 // Method java/lang/StringBuffer.toString:()Ljava/lang/String;
31: areturn
上面是先new StringBuilder,之后只是对这个StringBuilder进行append。 结论:当使用变量并循环拼接字符串时,应该使用StringBuilder的方式。