问题描述
写这篇文章是为了记录我这几天遇到的一个疑惑,并且顺藤摸瓜的学习一下javap命令。遇到的疑惑是这样的:我在看“使用枚举类型实现单列模式”的博客时,发现一些博客中写到的枚举类型的反编译结果包含的信息不尽相同:
一些对枚举类的反编译结果仅仅包含像我们正常编写的枚举类的一些信息,如使用IDEA,Java Decompiler;
而另一些反编译结果则完全不同:一方面枚举类成为了普通的class类,只是它继承了Enum类,枚举值都是public static final形式的对象;另一方面还多了static块,values(),valueof()这些方法。
源代码:
public enum Season {
SPIRNG,
SUMMER,
AUTUMN,
WINTER;
public String allSeasons() {
return SPIRNG.name() + " " + SUMMER.name() + " " + AUTUMN.name() +" " +WINTER.name();
}
}
使用javap -c Season.class命令反编译结果:
public final class Season extends java.lang.Enum<Season> {
public static final Season SPIRNG;
public static final Season SUMMER;
public static final Season AUTUMN;
public static final Season WINTER;
public static Season[] values();
Code:
0: getstatic #1 // Field $VALUES:[LSeason;
3: invokevirtual #2 // Method "[LSeason;".clone:()Ljava/lang/Object;
6: checkcast #3 // class "[LSeason;"
9: areturn
public static Season valueOf(java.lang.String);
Code:
0: ldc #4 // class Season
2: aload_0
3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #4 // class Season
9: areturn
public java.lang.String allSeasons();
Code:
0: new #7 // class java/lang/StringBuilder
3: dup
4: invokespecial #8 // Method java/lang/StringBuilder."<init>":()V
7: getstatic #9 // Field SPIRNG:LSeason;
10: invokevirtual #10 // Method name:()Ljava/lang/String;
13: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
16: ldc #12 // String
18: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: getstatic #13 // Field SUMMER:LSeason;
24: invokevirtual #10 // Method name:()Ljava/lang/String;
27: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
30: ldc #12 // String
32: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
35: getstatic #14 // Field AUTUMN:LSeason;
38: invokevirtual #10 // Method name:()Ljava/lang/String;
41: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
44: ldc #12 // String
46: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
49: getstatic #15 // Field WINTER:LSeason;
52: invokevirtual #10 // Method name:()Ljava/lang/String;
55: invokevirtual #11 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
58: invokevirtual #16 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
61: areturn
static {};
Code:
0: new #4 // class Season
3: dup
4: ldc #17 // String SPIRNG
6: iconst_0
7: invokespecial #18 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #9 // Field SPIRNG:LSeason;
13: new #4 // class Season
16: dup
17: ldc #19 // String SUMMER
19: iconst_1
20: invokespecial #18 // Method "<init>":(Ljava/lang/String;I)V
23: putstatic #13 // Field SUMMER:LSeason;
26: new #4 // class Season
29: dup
30: ldc #20 // String AUTUMN
32: iconst_2
33: invokespecial #18 // Method "<init>":(Ljava/lang/String;I)V
36: putstatic #14 // Field AUTUMN:LSeason;
39: new #4 // class Season
42: dup
43: ldc #21 // String WINTER
45: iconst_3
46: invokespecial #18 // Method "<init>":(Ljava/lang/String;I)V
49: putstatic #15 // Field WINTER:LSeason;
52: iconst_4
53: anewarray #4 // class Season
56: dup
57: iconst_0
58: getstatic #9 // Field SPIRNG:LSeason;
61: aastore
62: dup
63: iconst_1
64: getstatic #13 // Field SUMMER:LSeason;
67: aastore
68: dup
69: iconst_2
70: getstatic #14 // Field AUTUMN:LSeason;
73: aastore
74: dup
75: iconst_3
76: getstatic #15 // Field WINTER:LSeason;
79: aastore
80: putstatic #1 // Field $VALUES:[LSeason;
83: return
}
使用Java Decompiler工具的反编译结果:
public enum Season
{
SPIRNG, SUMMER, AUTUMN, WINTER;
private Season() {}
public String allSeasons()
{
return SPIRNG.name() + " " + SUMMER.name() + " " + AUTUMN.name() + " " + WINTER.name();
}
}
从上面反编译的结果我们总结差别如下
- javap反编译的结果中类的声明不同,它是一个继承了Enum类并且声明为final类型的类;
- javap反编译结果中包含的方法不同,它包含了static(),valueOf(),values()方法;
- javap反编译结果中的方法体不同,它的方法体使用jvm指令来描述。
javap命令
C:\Users\w00457192>javap -h
用法: javap <options> <classes>
其中, 可能的选项包括:
-? -h --help -help 输出此帮助消息
-version 版本信息
-v -verbose 输出附加信息
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示最终常量
--module <模块>, -m <模块> 指定包含要反汇编的类的模块
--module-path <路径> 指定查找应用程序模块的位置
--system <jdk> 指定查找系统模块的位置
--class-path <路径> 指定查找用户类文件的位置
-classpath <路径> 指定查找用户类文件的位置
-cp <路径> 指定查找用户类文件的位置
-bootclasspath <路径> 覆盖引导类文件的位置
从上面的信息我们可以看出javap -c是用于反汇编,而不是反编译。参考维基百科的解释如下:
反汇编器(disassembler)是一种将机器语言转换为汇编语言的计算机程序——这与汇编器的目的相反。反汇编器与反编译器不同,反编译器的目标是高级语言而非汇编语言。反汇编器的反汇编输出通常格式化为适合人类阅读,而非用作汇编器的输入源,因此它主要是一个逆向工程工具。
结论
现在我们可以理解为什么javap和Java Decompiler的输出中方法体有所不同了。
至于为什么Java Decompiler没有包含static块和values(),valueof()方法,我推测是因为反编译器的目的是反编译出源码,而我们可以确定的是static块和values()和valueof()这两个方法是编译器自己实现的,不是我们人为添加的,所以在反编译器看来这不属于源码的一部分,因此没有在编译的结果中包含这些方法。
本文章参考了
通过javap命令分析java汇编指令
反汇编器
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。