Article
java反编译工具使用记录
# 工具
# 差异
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循环
源码
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
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.
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循环
源码
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
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.
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;
# 内部类
源码
public class JavaDecompilerTest3 {
private class Test2 {}
Test2 test = new Test2();
}
JD-Core v0.3.5 JD-GUI v0.6.2
public class JavaDecompilerTest3
{
JavaDecompilerTest3.Test2 test = new JavaDecompilerTest3.Test2(null);
private class Test2
{
private Test2()
{
}
}
}
Jad v1.5.8e2.
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
$ 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 你就看不懂了!!!
Code #NUM:(ldc #53)后面的 #NUM 表示常量池里面的对象。dup: init方法没有返回值,所以需要压两次栈, http://stackoverflow.com/questions/12438567/java-bytecode-dupLocalVariableTable: start-length作用域,slot存储单元,http://hllvm.group.iteye.com/group/topic/25858
解析:要看懂try-catch-finally,必须关注 Exception table !
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:
$ 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
Related
Related posts
-
有一种自由叫做程序员的自由:把公众号文章镜像回自己的博客
2026-06-02
-
树莓派 OpenClaw Browser 看不见摸不着?给它配个 VNC 图形环境,踏实安心的Debug
2026-03-09
-
从使用者到创造者:用 AI 构建你的专属 VS Code 工具链
2026-02-27
-
杀鸡焉用牛刀:DuckDB 正取代部分 Spark 场景
2026-02-16