编译为 .class 并反编译回来时是否丢失了任何信息?

Is any information lost when compiling to a .class and decompiling back?

我认为java对反编译应该是比较友好的,也就是说.class文件本身有相当多的数据,仍然与原始源相当相似。

是否可以从反编译的 .class 文件构建原始源代码,这将是完全等效的,或者是否有任何 java 构造在过程中得到转换?

例如,我可以看到像增强的 for 循环这样的语法糖会导致问题。

我是否可以多次重复这个过程并仍然到达我开始的地方?

有几种反编译器可以执行此操作,这通常取决于代码的复杂性。根据我的经验,泛型有时会干扰反编译,但常规 POJO 可以 return 完全 class。

简单的回答是,是的,信息丢失了。

尤其是评论总是丢失。原始变量名是否丢失取决于编译代码时的编译器选项。使用调试选项 (-g) 编译的代码在反编译时将更具可读性。

反编译时,您得到的源代码在功能上等同,但与反编译时的源代码不完全相同。

考虑这两种方法:

public class MyTest {
    public String hi(String name) {
        return "Hi " + name;
    }

    public String howdy(String name) {
        return new StringBuilder().append("Hi ").append(name).toString();
    }
}

这里是反编译代码:

$ javap -c MyTest.class 
Compiled from "MyTest.java"
public class MyTest {
  public MyTest();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public java.lang.String hi(java.lang.String);
    Code:
       0: new           #2                  // class java/lang/StringBuilder
       3: dup           
       4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
       7: ldc           #4                  // String Hi 
       9: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      12: aload_1       
      13: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      16: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      19: areturn       

  public java.lang.String howdy(java.lang.String);
    Code:
       0: new           #2                  // class java/lang/StringBuilder
       3: dup           
       4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
       7: ldc           #4                  // String Hi 
       9: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      12: aload_1       
      13: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      16: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      19: areturn       
}

这只是语言中视觉上不同结构的一个简单示例,它们被编译为完全相同的字节码。因此,严格来说,您不能生成完全相同的代码。反编译器也不会看到局部变量名,除非编译器添加了调试信息。

还有像 ProGuard 这样的字节码混淆工具,声称无法自动反编译。

但是,如果反编译器成功生成了 java 代码,那么对其进行编译和反编译(使用相同的编译器和反编译器)应该会生成相同的 java 代码。