编译后的class文件在Java中保留了多少源码信息?
How much information of the source code does the compiled class file keep in Java?
当我自己的另一个项目的class文件被IntelliJ反编译时
(通过 Fernflower 反编译器),
与反编译代码相比,我对反编译代码的接近程度感到惊讶
源代码,
连方法局部变量名都和源码一样
我不知道 Java 编译过程是如何工作的,并且
JVM 是如何工作的,
我天真的理解是 public 东西的名字
编译后可能需要保留,
但是局部变量的名称,
它们只是便于人类阅读的助记符,
在他们的范围之外完全没用,
而且我认为 JVM 不需要这些信息。
所以,
这些信息是反编译器通过某种魔法简单地弄清楚的吗
还是编译后的 class 保留了很多信息,有什么用?
最终还是要看实际的编译器和具体的编译设置。
如您所述,JVM 本身不需要任何局部变量名。 (严格来说,它也不需要方法名称。甚至可以有 ,但我必须在规范中查找更多关于此的细节才能说得更深刻)。但是 class 文件可以包含超出 JVM 所需信息的附加调试信息。
标准 Java 编译器是 javac。并且文档中已经包含了一些关于可能的调试信息的提示:
-g
Generates all debugging information, including local variables. By default, only
line number and source file information is generated.
-g:none
Does not generate any debugging information.
-g:[keyword list]
Generates only some kinds of debugging information, specified by a comma separated list of keywords. Valid keywords are:
- source : Source file debugging information.
- lines: Line number debugging information.
- vars: Local variable debugging information.
可以举个例子试试:
public class ExampleClass {
public static void main(String[] args) {
ExampleClass exampleClass = new ExampleClass();
exampleClass.exampleMethod();
}
public void exampleMethod() {
String string = "This is an example";
for (int counter = 0; counter < 10; counter++) {
String localResult = string + counter;
System.out.println(localResult);
}
}
}
用
编译
javac ExampleClass.java -g:none
将生成一个 class 文件。使用
打印有关此 class 文件的信息
javap -c -v -l ExampleClass.class
(其中-c
表示反汇编输出,-v
表示输出要冗长,-l
表示要打印行号信息),输出如下:
public class ExampleClass
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #13.#22 // java/lang/Object."<init>":()V
#2 = Class #23 // ExampleClass
#3 = Methodref #2.#22 // ExampleClass."<init>":()V
#4 = Methodref #2.#24 // ExampleClass.exampleMethod:()V
#5 = String #25 // This is an example
#6 = Class #26 // java/lang/StringBuilder
#7 = Methodref #6.#22 // java/lang/StringBuilder."<init>":()V
#8 = Methodref #6.#27 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#9 = Methodref #6.#28 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
#10 = Methodref #6.#29 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#11 = Fieldref #30.#31 // java/lang/System.out:Ljava/io/PrintStream;
#12 = Methodref #32.#33 // java/io/PrintStream.println:(Ljava/lang/String;)V
#13 = Class #34 // java/lang/Object
#14 = Utf8 <init>
#15 = Utf8 ()V
#16 = Utf8 Code
#17 = Utf8 main
#18 = Utf8 ([Ljava/lang/String;)V
#19 = Utf8 exampleMethod
#20 = Utf8 StackMapTable
#21 = Class #35 // java/lang/String
#22 = NameAndType #14:#15 // "<init>":()V
#23 = Utf8 ExampleClass
#24 = NameAndType #19:#15 // exampleMethod:()V
#25 = Utf8 This is an example
#26 = Utf8 java/lang/StringBuilder
#27 = NameAndType #36:#37 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#28 = NameAndType #36:#38 // append:(I)Ljava/lang/StringBuilder;
#29 = NameAndType #39:#40 // toString:()Ljava/lang/String;
#30 = Class #41 // java/lang/System
#31 = NameAndType #42:#43 // out:Ljava/io/PrintStream;
#32 = Class #44 // java/io/PrintStream
#33 = NameAndType #45:#46 // println:(Ljava/lang/String;)V
#34 = Utf8 java/lang/Object
#35 = Utf8 java/lang/String
#36 = Utf8 append
#37 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#38 = Utf8 (I)Ljava/lang/StringBuilder;
#39 = Utf8 toString
#40 = Utf8 ()Ljava/lang/String;
#41 = Utf8 java/lang/System
#42 = Utf8 out
#43 = Utf8 Ljava/io/PrintStream;
#44 = Utf8 java/io/PrintStream
#45 = Utf8 println
#46 = Utf8 (Ljava/lang/String;)V
{
public ExampleClass();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #2 // class ExampleClass
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method exampleMethod:()V
12: return
public void exampleMethod();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=1
0: ldc #5 // String This is an example
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: bipush 10
8: if_icmpge 43
11: new #6 // class java/lang/StringBuilder
14: dup
15: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
18: aload_1
19: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: iload_2
23: invokevirtual #9 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
26: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: astore_3
30: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_3
34: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
37: iinc 2, 1
40: goto 5
43: return
StackMapTable: number_of_entries = 2
frame_type = 253 /* append */
offset_delta = 5
locals = [ class java/lang/String, int ]
frame_type = 250 /* chop */
offset_delta = 37
}
这是相当多的信息,但除了 class 本身的实际 结构 之外没有其他信息。
(你提到必须保留“public东西的名称。但是“私有[=96=的名称] stuff" 也必须保留 - 至少,为了反射。使用像 Class#getDeclaredFields
这样的方法,你仍然可以访问私有字段,例如 - 所以名称必须在某处可用)。
现在,相反的是用
编译它
javac ExampleClass.java -g
保留全部调试信息。如上所述打印结果产生
public class ExampleClass
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #13.#36 // java/lang/Object."<init>":()V
#2 = Class #37 // ExampleClass
#3 = Methodref #2.#36 // ExampleClass."<init>":()V
#4 = Methodref #2.#38 // ExampleClass.exampleMethod:()V
#5 = String #39 // This is an example
#6 = Class #40 // java/lang/StringBuilder
#7 = Methodref #6.#36 // java/lang/StringBuilder."<init>":()V
#8 = Methodref #6.#41 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#9 = Methodref #6.#42 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
#10 = Methodref #6.#43 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#11 = Fieldref #44.#45 // java/lang/System.out:Ljava/io/PrintStream;
#12 = Methodref #46.#47 // java/io/PrintStream.println:(Ljava/lang/String;)V
#13 = Class #48 // java/lang/Object
#14 = Utf8 <init>
#15 = Utf8 ()V
#16 = Utf8 Code
#17 = Utf8 LineNumberTable
#18 = Utf8 LocalVariableTable
#19 = Utf8 this
#20 = Utf8 LExampleClass;
#21 = Utf8 main
#22 = Utf8 ([Ljava/lang/String;)V
#23 = Utf8 args
#24 = Utf8 [Ljava/lang/String;
#25 = Utf8 exampleClass
#26 = Utf8 exampleMethod
#27 = Utf8 localResult
#28 = Utf8 Ljava/lang/String;
#29 = Utf8 counter
#30 = Utf8 I
#31 = Utf8 string
#32 = Utf8 StackMapTable
#33 = Class #49 // java/lang/String
#34 = Utf8 SourceFile
#35 = Utf8 ExampleClass.java
#36 = NameAndType #14:#15 // "<init>":()V
#37 = Utf8 ExampleClass
#38 = NameAndType #26:#15 // exampleMethod:()V
#39 = Utf8 This is an example
#40 = Utf8 java/lang/StringBuilder
#41 = NameAndType #50:#51 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#42 = NameAndType #50:#52 // append:(I)Ljava/lang/StringBuilder;
#43 = NameAndType #53:#54 // toString:()Ljava/lang/String;
#44 = Class #55 // java/lang/System
#45 = NameAndType #56:#57 // out:Ljava/io/PrintStream;
#46 = Class #58 // java/io/PrintStream
#47 = NameAndType #59:#60 // println:(Ljava/lang/String;)V
#48 = Utf8 java/lang/Object
#49 = Utf8 java/lang/String
#50 = Utf8 append
#51 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#52 = Utf8 (I)Ljava/lang/StringBuilder;
#53 = Utf8 toString
#54 = Utf8 ()Ljava/lang/String;
#55 = Utf8 java/lang/System
#56 = Utf8 out
#57 = Utf8 Ljava/io/PrintStream;
#58 = Utf8 java/io/PrintStream
#59 = Utf8 println
#60 = Utf8 (Ljava/lang/String;)V
{
public ExampleClass();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LExampleClass;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #2 // class ExampleClass
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method exampleMethod:()V
12: return
LineNumberTable:
line 4: 0
line 5: 8
line 6: 12
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 args [Ljava/lang/String;
8 5 1 exampleClass LExampleClass;
public void exampleMethod();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=1
0: ldc #5 // String This is an example
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: bipush 10
8: if_icmpge 43
11: new #6 // class java/lang/StringBuilder
14: dup
15: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
18: aload_1
19: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: iload_2
23: invokevirtual #9 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
26: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: astore_3
30: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_3
34: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
37: iinc 2, 1
40: goto 5
43: return
LineNumberTable:
line 9: 0
line 10: 3
line 11: 11
line 12: 30
line 10: 37
line 14: 43
LocalVariableTable:
Start Length Slot Name Signature
30 7 3 localResult Ljava/lang/String;
5 38 2 counter I
0 44 0 this LExampleClass;
3 41 1 string Ljava/lang/String;
StackMapTable: number_of_entries = 2
frame_type = 253 /* append */
offset_delta = 5
locals = [ class java/lang/String, int ]
frame_type = 250 /* chop */
offset_delta = 37
}
SourceFile: "ExampleClass.java"
主要区别是
- class包含源文件名
- 常量池有 许多 个条目
- 这些方法包含一个
LineNumberTable
和一个 LocalVariableTable
。
例如,考虑 exampleMethod()
:
LineNumberTable:
line 9: 0
line 10: 3
line 11: 11
line 12: 30
line 10: 37
line 14: 43
LocalVariableTable:
Start Length Slot Name Signature
30 7 3 localResult Ljava/lang/String;
5 38 2 counter I
0 44 0 this LExampleClass;
3 41 1 string Ljava/lang/String;
有关这些属性结构的详细信息在 LineNumberTable
and the LocalVariableTable
的文档中给出。
对于LineNumberTable
,它表示
It may be used by debuggers to determine which part of the code array corresponds to a given line number in the original source file.
对于LocalVariableTable
,它表示
It may be used by debuggers to determine the value of a given local variable during the execution of a method.
在javap
的输出中,局部变量的names已经解析。然而,table本身包含的实际信息只是常量池中的索引(这就是为什么它在保留调试信息时具有更多条目的原因)。例如,localResult
变量的条目显示为
30 7 3 localResult Ljava/lang/String;
虽然它实际上只包含对条目的引用
#27 = Utf8 localResult
常量池的
So, are these information simply figured out by the de-compiler through some magic or dose the compiled class retain a lot of information and what for?
如上所示,编译后的class可以保留很多信息。毕竟,IDE 的主要目的之一是为 调试器 提供一个漂亮的可视化界面。因此,大多数由 IDE 以一种或另一种方式触发的编译器将默认尝试保留尽可能多的调试信息。
当我自己的另一个项目的class文件被IntelliJ反编译时 (通过 Fernflower 反编译器), 与反编译代码相比,我对反编译代码的接近程度感到惊讶 源代码, 连方法局部变量名都和源码一样
我不知道 Java 编译过程是如何工作的,并且 JVM 是如何工作的, 我天真的理解是 public 东西的名字 编译后可能需要保留, 但是局部变量的名称, 它们只是便于人类阅读的助记符, 在他们的范围之外完全没用, 而且我认为 JVM 不需要这些信息。
所以, 这些信息是反编译器通过某种魔法简单地弄清楚的吗 还是编译后的 class 保留了很多信息,有什么用?
最终还是要看实际的编译器和具体的编译设置。
如您所述,JVM 本身不需要任何局部变量名。 (严格来说,它也不需要方法名称。甚至可以有
标准 Java 编译器是 javac。并且文档中已经包含了一些关于可能的调试信息的提示:
-g
Generates all debugging information, including local variables. By default, only line number and source file information is generated.
-g:none
Does not generate any debugging information.
-g:[keyword list]
Generates only some kinds of debugging information, specified by a comma separated list of keywords. Valid keywords are:
- source : Source file debugging information.
- lines: Line number debugging information.
- vars: Local variable debugging information.
可以举个例子试试:
public class ExampleClass {
public static void main(String[] args) {
ExampleClass exampleClass = new ExampleClass();
exampleClass.exampleMethod();
}
public void exampleMethod() {
String string = "This is an example";
for (int counter = 0; counter < 10; counter++) {
String localResult = string + counter;
System.out.println(localResult);
}
}
}
用
编译javac ExampleClass.java -g:none
将生成一个 class 文件。使用
打印有关此 class 文件的信息javap -c -v -l ExampleClass.class
(其中-c
表示反汇编输出,-v
表示输出要冗长,-l
表示要打印行号信息),输出如下:
public class ExampleClass
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #13.#22 // java/lang/Object."<init>":()V
#2 = Class #23 // ExampleClass
#3 = Methodref #2.#22 // ExampleClass."<init>":()V
#4 = Methodref #2.#24 // ExampleClass.exampleMethod:()V
#5 = String #25 // This is an example
#6 = Class #26 // java/lang/StringBuilder
#7 = Methodref #6.#22 // java/lang/StringBuilder."<init>":()V
#8 = Methodref #6.#27 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#9 = Methodref #6.#28 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
#10 = Methodref #6.#29 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#11 = Fieldref #30.#31 // java/lang/System.out:Ljava/io/PrintStream;
#12 = Methodref #32.#33 // java/io/PrintStream.println:(Ljava/lang/String;)V
#13 = Class #34 // java/lang/Object
#14 = Utf8 <init>
#15 = Utf8 ()V
#16 = Utf8 Code
#17 = Utf8 main
#18 = Utf8 ([Ljava/lang/String;)V
#19 = Utf8 exampleMethod
#20 = Utf8 StackMapTable
#21 = Class #35 // java/lang/String
#22 = NameAndType #14:#15 // "<init>":()V
#23 = Utf8 ExampleClass
#24 = NameAndType #19:#15 // exampleMethod:()V
#25 = Utf8 This is an example
#26 = Utf8 java/lang/StringBuilder
#27 = NameAndType #36:#37 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#28 = NameAndType #36:#38 // append:(I)Ljava/lang/StringBuilder;
#29 = NameAndType #39:#40 // toString:()Ljava/lang/String;
#30 = Class #41 // java/lang/System
#31 = NameAndType #42:#43 // out:Ljava/io/PrintStream;
#32 = Class #44 // java/io/PrintStream
#33 = NameAndType #45:#46 // println:(Ljava/lang/String;)V
#34 = Utf8 java/lang/Object
#35 = Utf8 java/lang/String
#36 = Utf8 append
#37 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#38 = Utf8 (I)Ljava/lang/StringBuilder;
#39 = Utf8 toString
#40 = Utf8 ()Ljava/lang/String;
#41 = Utf8 java/lang/System
#42 = Utf8 out
#43 = Utf8 Ljava/io/PrintStream;
#44 = Utf8 java/io/PrintStream
#45 = Utf8 println
#46 = Utf8 (Ljava/lang/String;)V
{
public ExampleClass();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #2 // class ExampleClass
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method exampleMethod:()V
12: return
public void exampleMethod();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=1
0: ldc #5 // String This is an example
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: bipush 10
8: if_icmpge 43
11: new #6 // class java/lang/StringBuilder
14: dup
15: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
18: aload_1
19: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: iload_2
23: invokevirtual #9 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
26: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: astore_3
30: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_3
34: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
37: iinc 2, 1
40: goto 5
43: return
StackMapTable: number_of_entries = 2
frame_type = 253 /* append */
offset_delta = 5
locals = [ class java/lang/String, int ]
frame_type = 250 /* chop */
offset_delta = 37
}
这是相当多的信息,但除了 class 本身的实际 结构 之外没有其他信息。
(你提到必须保留“public东西的名称。但是“私有[=96=的名称] stuff" 也必须保留 - 至少,为了反射。使用像 Class#getDeclaredFields
这样的方法,你仍然可以访问私有字段,例如 - 所以名称必须在某处可用)。
现在,相反的是用
编译它javac ExampleClass.java -g
保留全部调试信息。如上所述打印结果产生
public class ExampleClass
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #13.#36 // java/lang/Object."<init>":()V
#2 = Class #37 // ExampleClass
#3 = Methodref #2.#36 // ExampleClass."<init>":()V
#4 = Methodref #2.#38 // ExampleClass.exampleMethod:()V
#5 = String #39 // This is an example
#6 = Class #40 // java/lang/StringBuilder
#7 = Methodref #6.#36 // java/lang/StringBuilder."<init>":()V
#8 = Methodref #6.#41 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#9 = Methodref #6.#42 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
#10 = Methodref #6.#43 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#11 = Fieldref #44.#45 // java/lang/System.out:Ljava/io/PrintStream;
#12 = Methodref #46.#47 // java/io/PrintStream.println:(Ljava/lang/String;)V
#13 = Class #48 // java/lang/Object
#14 = Utf8 <init>
#15 = Utf8 ()V
#16 = Utf8 Code
#17 = Utf8 LineNumberTable
#18 = Utf8 LocalVariableTable
#19 = Utf8 this
#20 = Utf8 LExampleClass;
#21 = Utf8 main
#22 = Utf8 ([Ljava/lang/String;)V
#23 = Utf8 args
#24 = Utf8 [Ljava/lang/String;
#25 = Utf8 exampleClass
#26 = Utf8 exampleMethod
#27 = Utf8 localResult
#28 = Utf8 Ljava/lang/String;
#29 = Utf8 counter
#30 = Utf8 I
#31 = Utf8 string
#32 = Utf8 StackMapTable
#33 = Class #49 // java/lang/String
#34 = Utf8 SourceFile
#35 = Utf8 ExampleClass.java
#36 = NameAndType #14:#15 // "<init>":()V
#37 = Utf8 ExampleClass
#38 = NameAndType #26:#15 // exampleMethod:()V
#39 = Utf8 This is an example
#40 = Utf8 java/lang/StringBuilder
#41 = NameAndType #50:#51 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#42 = NameAndType #50:#52 // append:(I)Ljava/lang/StringBuilder;
#43 = NameAndType #53:#54 // toString:()Ljava/lang/String;
#44 = Class #55 // java/lang/System
#45 = NameAndType #56:#57 // out:Ljava/io/PrintStream;
#46 = Class #58 // java/io/PrintStream
#47 = NameAndType #59:#60 // println:(Ljava/lang/String;)V
#48 = Utf8 java/lang/Object
#49 = Utf8 java/lang/String
#50 = Utf8 append
#51 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#52 = Utf8 (I)Ljava/lang/StringBuilder;
#53 = Utf8 toString
#54 = Utf8 ()Ljava/lang/String;
#55 = Utf8 java/lang/System
#56 = Utf8 out
#57 = Utf8 Ljava/io/PrintStream;
#58 = Utf8 java/io/PrintStream
#59 = Utf8 println
#60 = Utf8 (Ljava/lang/String;)V
{
public ExampleClass();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LExampleClass;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #2 // class ExampleClass
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_1
8: aload_1
9: invokevirtual #4 // Method exampleMethod:()V
12: return
LineNumberTable:
line 4: 0
line 5: 8
line 6: 12
LocalVariableTable:
Start Length Slot Name Signature
0 13 0 args [Ljava/lang/String;
8 5 1 exampleClass LExampleClass;
public void exampleMethod();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=1
0: ldc #5 // String This is an example
2: astore_1
3: iconst_0
4: istore_2
5: iload_2
6: bipush 10
8: if_icmpge 43
11: new #6 // class java/lang/StringBuilder
14: dup
15: invokespecial #7 // Method java/lang/StringBuilder."<init>":()V
18: aload_1
19: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: iload_2
23: invokevirtual #9 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
26: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: astore_3
30: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_3
34: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
37: iinc 2, 1
40: goto 5
43: return
LineNumberTable:
line 9: 0
line 10: 3
line 11: 11
line 12: 30
line 10: 37
line 14: 43
LocalVariableTable:
Start Length Slot Name Signature
30 7 3 localResult Ljava/lang/String;
5 38 2 counter I
0 44 0 this LExampleClass;
3 41 1 string Ljava/lang/String;
StackMapTable: number_of_entries = 2
frame_type = 253 /* append */
offset_delta = 5
locals = [ class java/lang/String, int ]
frame_type = 250 /* chop */
offset_delta = 37
}
SourceFile: "ExampleClass.java"
主要区别是
- class包含源文件名
- 常量池有 许多 个条目
- 这些方法包含一个
LineNumberTable
和一个LocalVariableTable
。
例如,考虑 exampleMethod()
:
LineNumberTable:
line 9: 0
line 10: 3
line 11: 11
line 12: 30
line 10: 37
line 14: 43
LocalVariableTable:
Start Length Slot Name Signature
30 7 3 localResult Ljava/lang/String;
5 38 2 counter I
0 44 0 this LExampleClass;
3 41 1 string Ljava/lang/String;
有关这些属性结构的详细信息在 LineNumberTable
and the LocalVariableTable
的文档中给出。
对于LineNumberTable
,它表示
It may be used by debuggers to determine which part of the code array corresponds to a given line number in the original source file.
对于LocalVariableTable
,它表示
It may be used by debuggers to determine the value of a given local variable during the execution of a method.
在javap
的输出中,局部变量的names已经解析。然而,table本身包含的实际信息只是常量池中的索引(这就是为什么它在保留调试信息时具有更多条目的原因)。例如,localResult
变量的条目显示为
30 7 3 localResult Ljava/lang/String;
虽然它实际上只包含对条目的引用
#27 = Utf8 localResult
常量池的
So, are these information simply figured out by the de-compiler through some magic or dose the compiled class retain a lot of information and what for?
如上所示,编译后的class可以保留很多信息。毕竟,IDE 的主要目的之一是为 调试器 提供一个漂亮的可视化界面。因此,大多数由 IDE 以一种或另一种方式触发的编译器将默认尝试保留尽可能多的调试信息。