Python中文网

一个关于 编程问题的解答网站.

有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java反编译foreach循环

反编译。下面的类文件为每个循环生成有趣的结果

源-主。爪哇:

public class Main {
    public static void main(String[] args) {
        String[] names = new String[3];
        int var3 = 3;

        for (String name : names) {
            System.out.println(name);
        }
    }
}

Result-Main。课程:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

public class Main {
    public Main() {
    }

    public static void main(String[] args) {
        String[] names = new String[3];
        int var3 = true;
        String[] var3 = names;
        int var4 = names.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            String name = var3[var5];
            System.out.println(name);
        }

    }
}

这个文件是用IntelliJ IDEA反编译的

  • 为什么将true分配给未使用的int
  • 为什么重新声明var3变量

这代表反编译程序是个错误吗


共 (1) 个答案

  1. # 1 楼答案

    在字节码级别,没有局部变量的正式声明,至少没有源代码中已知的方式。一个方法声明了同时存在的局部变量的最大数量或为它们保留的“时隙”。当一个局部变量被分配了一个实际值(通过“slot”索引)并且至少存在于该值的最后一次读取时,该局部变量就开始生效

    通过这些操作,无法识别变量的作用域何时结束,或者两个作用域不相交的变量是否共享一个时隙(与对同一变量的多个赋值相比)。如果它们有完全不兼容的类型,它们的赋值会给出提示

    为了帮助调试,有一个可选的代码属性提供有关声明的局部变量及其作用域的提示,但这并不要求完整,也不会影响JVM执行字节码的方式。但在这里,该属性似乎已经存在,并已被反编译器使用

    当我用javac -g编译示例代码时,我得到

    public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=7, args_size=1
         0: iconst_3
         1: anewarray     #2        // class java/lang/String
         4: astore_1
         5: iconst_3
         6: istore_2
         7: aload_1
         8: astore_3
         9: aload_3
        10: arraylength
        11: istore        4
        13: iconst_0
        14: istore        5
        16: iload         5
        18: iload         4
        20: if_icmpge     43
        23: aload_3
        24: iload         5
        26: aaload
        27: astore        6
        29: getstatic     #3        // Field java/lang/System.out:Ljava/io/PrintStream;
        32: aload         6
        34: invokevirtual #4        // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        37: iinc          5, 1
        40: goto          16
        43: return
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           29       8     6  name   Ljava/lang/String;
            0      44     0  args   [Ljava/lang/String;
            5      39     1 names   [Ljava/lang/String;
            7      37     2  var3   I
    

    声明的变量args(方法参数)、namesvar3name按顺序分配给变量索引0126

    有些合成变量没有声明

    • 在索引3处保存循环正在迭代的数组的引用
    • 在索引4处保存数组长度
    • 在索引5处保存int索引变量,该变量将在循环中递增

    反编译器似乎有一个简单的策略来处理LocalVariableTable中不包含的变量。它生成一个由前缀"var"和堆栈框架内的索引组成的名称。因此,它为上述合成变量生成了名称var3var4var5,并且不关心这些生成的名称与显式声明的名称之间是否存在名称冲突,即var3

    现在,不清楚为什么反编译器会为int变量生成true赋值,但它有助于了解Java字节码中没有专用的boolean处理指令,而是以与int值相同的方式处理boolean值。它需要适当的元信息,比如变量声明,来理解值何时应该被解释为boolean值。也许,上面提到的名称冲突导致反编译器混淆了以后的变量类型,最终考虑值类型不是^ {< CD13> },然后回落到将它当作^ {CD22}}。但这只是猜测;可能还有一个完全无关的bug