工具
差异
JD-Core还是比较与时俱进的,对泛型/foreach等新的语法都支持。Jad则只支持到only 45.3(1.1), 46.0 and 47.0(1.3)。
任何事物都不是完美的,结合两个工具来看反编译后的源码能更好的回归源码的真相。
JD-Core解析双for循环是存在问题!结合jad可能更好的明白源码。
内部类解析都 不够好,JD-Core相对错误少一些
对比
这里就[for循环]/[双层for循环]/[内部类]进行对比。
单for循环
源码
1
2
3
4
5
6
7
List<String> list = Arrays.asList("abc", "bcd");
public void testForEach() {
for (String ele : list) { // for编译成字节码后使用iterator的形式
System.out.println(ele);
}
}
JD-Core v0.3.5 JD-GUI v0.6.2
1
2
3
4
5
6
List<String> list = Arrays.asList(new String[] { "abc", "bcd" });
public void testForEach() {
for (String ele : this.list)
System.out.println(ele);
}
Jad v1.5.8e2.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public JavaDecompilerTest()
{
list = Arrays.asList(new String[] {
"abc", "bcd"
});
}
public void testForEach()
{
String ele;
for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(ele))
ele = (String)iterator.next();
}
List list;
双层for循环
源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
{
Map<String, String> map = new HashMap<String, String>();
map.put("12", "23");
list.add(map);
}
public void testForIndex() {
for (int i = 0; i < list.size(); i++) {
Map<String, String> map = list.get(i);
for (String key : map.keySet())
System.out.println(map.get(key));
}
}
public void testForEach() {
for (Map<String, String> map : list) {
for (String key : map.keySet())
System.out.println(map.get(key));
}
}
JD-Core v0.3.5 JD-GUI v0.6.2
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
List<Map<String, String>> list;
public JavaDecompilerTest2()
{
this.list = new ArrayList();
Map map = new HashMap();
map.put("12", "23");
this.list.add(map);
}
public void testForIndex() {
for (int i = 0; i < this.list.size(); i++) {
Map map = (Map)this.list.get(i);
for (String key : map.keySet())
System.out.println((String)map.get(key));
}
}
public void testForEach()
{
Iterator localIterator2;
for (Iterator localIterator1 = this.list.iterator(); localIterator1.hasNext();
localIterator2.hasNext())
{
Map map = (Map)localIterator1.next();
localIterator2 = map.keySet().iterator(); continue; String key = (String)localIterator2.next();
System.out.println((String)map.get(key));
}
}
Jad v1.5.8e2.
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
public JavaDecompilerTest2()
{
list = new ArrayList();
Map map = new HashMap();
map.put("12", "23");
list.add(map);
}
public void testForIndex()
{
for(int i = 0; i < list.size(); i++)
{
Map map = (Map)list.get(i);
String key;
for(Iterator iterator = map.keySet().iterator(); iterator.hasNext(); System.out.println((String)map.get(key)))
key = (String)iterator.next();
}
}
public void testForEach()
{
for(Iterator iterator = list.iterator(); iterator.hasNext();)
{
Map map = (Map)iterator.next();
String key;
for(Iterator iterator1 = map.keySet().iterator(); iterator1.hasNext(); System.out.println((String)map.get(key)))
key = (String)iterator1.next();
}
}
List list;
内部类
源码
1
2
3
4
5
6
7
public class JavaDecompilerTest3 {
private class Test2 {}
Test2 test = new Test2();
}
JD-Core v0.3.5 JD-GUI v0.6.2
1
2
3
4
5
6
7
8
9
10
11
public class JavaDecompilerTest3
{
JavaDecompilerTest3.Test2 test = new JavaDecompilerTest3.Test2(null);
private class Test2
{
private Test2()
{
}
}
}
Jad v1.5.8e2.
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
public class JavaDecompilerTest3
{
private class Test2
{
final JavaDecompilerTest3 this$0;
private Test2()
{
this$0 = JavaDecompilerTest3.this;
super();
}
Test2(Test2 test2)
{
this();
}
}
public JavaDecompilerTest3()
{
test = new Test2(null);
}
Test2 test;
}
后记
工具javap可以查看class的方法签名,通过jd-gui和jad可以反编译得到源码,如果代码没有混淆的话,多半就能了解代码的功能了。
读jdk7的try-resource的字节码
先了解下字节码的规范:Compiling for the Java Virtual Machine
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
$ javap -public -c -v ByteCodeTest.class
Classfile /E:/test/target/test-classes/com/ByteCodeTest.class
Last modified 2016-5-6; size 992 bytes
MD5 checksum ee7912d638a98f9a4a61d726e08c08f0
Compiled from "ByteCodeTest.java"
public class com.ByteCodeTest
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Class #2 // com/ByteCodeTest
#2 = Utf8 com/ByteCodeTest
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Methodref #3.#9 // java/lang/Object."<init>":()V
#9 = NameAndType #5:#6 // "<init>":()V
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/ByteCodeTest;
#14 = Utf8 tryResourceWithFinally
#15 = Utf8 Exceptions
#16 = Class #17 // java/io/FileNotFoundException
#17 = Utf8 java/io/FileNotFoundException
#18 = Class #19 // java/io/IOException
#19 = Utf8 java/io/IOException
#20 = Utf8 RuntimeVisibleAnnotations
#21 = Utf8 Lorg/junit/Test;
#22 = Class #23 // java/io/FileInputStream
#23 = Utf8 java/io/FileInputStream
#24 = String #25 //
#25 = Utf8
#26 = Methodref #22.#27 // java/io/FileInputStream."<init>":(Ljava/lang/String;)V
#27 = NameAndType #5:#28 // "<init>":(Ljava/lang/String;)V
#28 = Utf8 (Ljava/lang/String;)V
#29 = Fieldref #30.#32 // java/lang/System.out:Ljava/io/PrintStream;
#30 = Class #31 // java/lang/System
#31 = Utf8 java/lang/System
#32 = NameAndType #33:#34 // out:Ljava/io/PrintStream;
#33 = Utf8 out
#34 = Utf8 Ljava/io/PrintStream;
#35 = String #36 // ing
#36 = Utf8 ing
#37 = Methodref #38.#40 // java/io/PrintStream.println:(Ljava/lang/String;)V
#38 = Class #39 // java/io/PrintStream
#39 = Utf8 java/io/PrintStream
#40 = NameAndType #41:#28 // println:(Ljava/lang/String;)V
#41 = Utf8 println
#42 = Methodref #43.#45 // java/io/InputStream.close:()V
#43 = Class #44 // java/io/InputStream
#44 = Utf8 java/io/InputStream
#45 = NameAndType #46:#6 // close:()V
#46 = Utf8 close
#47 = Methodref #48.#50 // java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
#48 = Class #49 // java/lang/Throwable
#49 = Utf8 java/lang/Throwable
#50 = NameAndType #51:#52 // addSuppressed:(Ljava/lang/Throwable;)V
#51 = Utf8 addSuppressed
#52 = Utf8 (Ljava/lang/Throwable;)V
#53 = Utf8 in
#54 = Utf8 Ljava/io/InputStream;
#55 = Utf8 StackMapTable
#56 = Utf8 SourceFile
#57 = Utf8 ByteCodeTest.java
{
public com.ByteCodeTest();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 10: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/ByteCodeTest;
public void tryResourceWithFinally() throws java.io.FileNotFoundException, java.io.IOException;
descriptor: ()V
flags: ACC_PUBLIC
Exceptions:
throws java.io.FileNotFoundException, java.io.IOException
RuntimeVisibleAnnotations:
0: #21()
Code:
stack=3, locals=4, args_size=1
0: aconst_null
1: astore_1
2: aconst_null
3: astore_2
4: new #22 // class java/io/FileInputStream
7: dup
8: ldc #24 // String
10: invokespecial #26 // Method java/io/FileInputStream."<init>":(Ljava/lang/String;)V
13: astore_3
14: getstatic #29 // Field java/lang/System.out:Ljava/io/PrintStream;
17: ldc #35 // String ing
19: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
22: aload_3
23: ifnull 66
26: aload_3
27: invokevirtual #42 // Method java/io/InputStream.close:()V
30: goto 66
33: astore_1
34: aload_3
35: ifnull 42
38: aload_3
39: invokevirtual #42 // Method java/io/InputStream.close:()V
42: aload_1
43: athrow
44: astore_2
45: aload_1
46: ifnonnull 54
49: aload_2
50: astore_1
51: goto 64
54: aload_1
55: aload_2
56: if_acmpeq 64
59: aload_1
60: aload_2
61: invokevirtual #47 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
64: aload_1
65: athrow
66: return
Exception table:
from to target type
14 22 33 any
4 44 44 any
LineNumberTable:
line 14: 0
line 15: 14
line 16: 22
line 17: 66
LocalVariableTable:
Start Length Slot Name Signature
0 67 0 this Lcom/ByteCodeTest;
14 28 3 in Ljava/io/InputStream;
StackMapTable: number_of_entries = 6
frame_type = 255 /* full_frame */
offset_delta = 33
locals = [ class com/ByteCodeTest, class java/lang/Throwable, class java/lang/Throwable, class java/io/InputStream ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 8
frame_type = 65 /* same_locals_1_stack_item */
stack = [ class java/lang/Throwable ]
frame_type = 9 /* same */
frame_type = 9 /* same */
frame_type = 249 /* chop */
offset_delta = 1
}
SourceFile: "ByteCodeTest.java"
public void tryResourceWithFinally() throws FileNotFoundException, IOException {
try (InputStream in = new FileInputStream("")) {
System.out.println("ing");
}
}
异常eclipse提示啥直接抛出来,不要 throws Exception
,不然前面的 astore_1/astore_2 你就看不懂了!!!
解析:要看懂try-catch-finally,必须关注 Exception table !
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
any a1 = null
any a2 = null
any a3/in = new FileInputStream("")
System.out.println("ing")
catch-1(33):
// noop
if(a3/in != null){
in.close()
}
rethrow a1
if(a3/in != null){
in.close()
}
catch-2(44):
// noop
if(a3/in != null){
in.close()
}
if(a1 == null){
// noop
a1 = a2
rethrow a1
}
if (a1 == a2){
rethrow a1
}
a1.addSuppressed(a2)
rethrow a1
在添加自定义的finally:
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
$ javap -public -c ByteCodeTest.class
Compiled from "ByteCodeTest.java"
public class com.ByteCodeTest {
public com.ByteCodeTest();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public void tryResourceWithFinally() throws java.io.FileNotFoundException, java.io.IOException;
Code:
0: aconst_null
1: astore_1
2: aconst_null
3: astore_2
4: new #22 // class java/io/FileInputStream
7: dup
8: ldc #24 // String
10: invokespecial #26 // Method java/io/FileInputStream."<init>":(Ljava/lang/String;)V
13: astore_3
14: getstatic #29 // Field java/lang/System.out:Ljava/io/PrintStream;
17: ldc #35 // String ing
19: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
22: aload_3
23: ifnull 79
26: aload_3
27: invokevirtual #42 // Method java/io/InputStream.close:()V
30: goto 79
33: astore_1
34: aload_3
35: ifnull 42
38: aload_3
39: invokevirtual #42 // Method java/io/InputStream.close:()V
42: aload_1
43: athrow
44: astore_2
45: aload_1
46: ifnonnull 54
49: aload_2
50: astore_1
51: goto 64
54: aload_1
55: aload_2
56: if_acmpeq 64
59: aload_1
60: aload_2
61: invokevirtual #47 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
64: aload_1
65: athrow
66: astore 4
68: getstatic #29 // Field java/lang/System.out:Ljava/io/PrintStream;
71: ldc #53 // String finish
73: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
76: aload 4
78: athrow
79: getstatic #29 // Field java/lang/System.out:Ljava/io/PrintStream;
82: ldc #53 // String finish
84: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
87: return
Exception table:
from to target type
14 22 33 any
4 44 44 any
0 66 66 any
}
public void tryResourceWithFinally() throws FileNotFoundException, IOException {
try (InputStream in = new FileInputStream("")) {
System.out.println("ing");
} finally {
System.out.println("finish");
}
}
在异常表里面多了一条记录,比上面的复杂点。
–END