为什么不同的Java反编译器显示不同的源代码?

时间:2014-12-22 06:09:13

标签: java compiler-construction decompiling decompiler

我试图通过反编译.class文件来了解Java编译器的工作原理。我使用过Java反编译器(http://jd.benow.ca/)和showmycode(http://www.showmycode.com/) 它们在.java文件中显示了不同的源代码。 为什么?我应该相信哪一个。

.java文件

class HelloWorld{
    public static void main(String[] args){
        System.out.println("Hello, World!");
    }
}

.class文件,由java反编译器反编译:

import java.io.PrintStream;

class HelloWorld
{
  public static void main(String[] paramArrayOfString)
  {
    System.out.println("Hello, World!");
  }
}

.class文件,由showmycode解密

import java.io.PrintStream;
class HelloWorld {
   HelloWorld() {
   } 
   public static void main(string args[]) 
   {
       system.out.println("Hello, World!"); 
   }
}

2 个答案:

答案 0 :(得分:8)

反编译器无法重新创建原始源代码,它只能创建一个新的源代码,它将编译成与原始源代码相同的二进制文件。

(假设 showmycode 将解决他们的案例问题,请参见下文)三个源代码 - 原始代码,由 java反编译器创建的代码和由 showmycode 幂等。它们以不同的方式编写,但做同样的事情。两个反编译都是对的。

以下是解释的差异:

  • 构造函数存在/不存在:每个类都有一个构造函数。如果程序员不提供构造函数,编译器将生成一个。一个智能反编译器会识别这个,并假设一个为空的构造函数生成它,因此从反编译的源代码中省略它。
  • []对数组的位置:声明String[] fooString foo[]是相同的。我建议总是去String[] foo,因为在Java中(而不是C),作为数组是类型的属性,而不是变量。
  • 格式化/放置{},缩进等:大多数情况下都没有关系。调试信息(堆栈跟踪的行数)可能有点重要,但这不是代码的重要行为的一部分,这是我们感兴趣的。
  • 自动变量的名称(包含参数变量的局部变量)在二进制文件中不可用,因此反编译器必须在反编译时创建新名称。

请注意, showmycode 似乎与类型名称的情况有关。 Java区分大小写,它必须是SystemString,而不是systemstring。似乎 showmycode 会出错,这意味着除非您手动修复所有这些类型名称,否则无法再次编译代码。

showmycode 的另一个问题是它如何处理varargs方法。我将main的签名更改为public static void main(String... args)以查看我得到的内容,并且我得到了public static transient void main(string args[]),但是没有编译。现在反编译器需要生成正确的,可编译的源代码, showmycode 不会。

答案 1 :(得分:0)

因为有几种不同的方式来编写大多数语句,所有这些都归结为相同的目标代码。通常没有单一的正确反编译。