Java 语法糖(三): 对数组进行 for each 操作

正文

一个简单的 for each 操作

我们写一个简单的 Main.java 来对数组进行 for each 操作(Main.java 的内容如下)

public class Main {
    public int f(int[] nums) {
        int sum = 0;
        for (int num : nums) {
            sum += num;
        }   
        return sum;
    }
}

执行如下命令可以看到字节码中的内容

javac Main.java
javap -cp . -p -v 'Main'

完整的内容较长,和 f(...) 直接相关的部分如下

  public int f(int[]);
    descriptor: ([I)I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=7, args_size=2
         0: iconst_0
         1: istore_2
         2: aload_1
         3: astore_3
         4: aload_3
         5: arraylength
         6: istore        4
         8: iconst_0
         9: istore        5
        11: iload         5
        13: iload         4
        15: if_icmpge     35
        18: aload_3
        19: iload         5
        21: iaload
        22: istore        6
        24: iload_2
        25: iload         6
        27: iadd
        28: istore_2
        29: iinc          5, 1
        32: goto          11
        35: iload_2
        36: ireturn
      LineNumberTable:
        line 3: 0
        line 4: 2
        line 5: 24
        line 4: 29
        line 7: 35
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 11
          locals = [ class Main, class "[I", int, class "[I", int, int ]
          stack = []
        frame_type = 248 /* chop */
          offset_delta = 23

内容有点多,还是借助 cfr 来分析吧。
cfr-0.148.jar 为例,执行如下命令,就能看到编译器操作之后的 for each 是什么样子了。

java -jar cfr-0.148.jar Main --arrayiter false

执行结果如下

/*
 * Decompiled with CFR 0.148.
 */
public class Main {
    public int f(int[] arrn) {
        int n = 0;
        int[] arrn2 = arrn;
        int n2 = arrn2.length;
        for (int i = 0; i < n2; ++i) {
            int n3 = arrn2[i];
            n += n3;
        }
        return n;
    }
}

由此可见,编译器会自动将针对数组的 for each 操作转化为普通的 for 循环。

有了 cfr 的帮助,我来尝试一下解释 f(...) 对应的字节码指令。

行号 字节码指令 简要的解释
0 iconst_0 将常数0加载到操作数栈栈顶
1 istore_2 将操作数栈栈顶的值保存到2slot

以上指令与如下 java 代码对应

int n = 0;

行号 字节码指令 简要的解释
2 aload_1 1slot的值加载到操作数栈栈顶
3 astore_3 将操作数栈栈顶的值保存到3slot

以上指令与如下 java 代码对应

int[] arrn2 = arrn;

行号 字节码指令 简要的解释
4 aload_3 3slot中的值加载到操作数栈栈顶
5 arraylength 操作数栈栈顶的元素是一个数组的引用,求出这个数组的 length
6 istore 4 将操作数栈栈顶的值保存到4slot

以上指令与如下 java 代码对应

int n2 = arrn2.length;

行号 字节码指令 简要的解释
8 iconst_0 将常数0加载到操作数栈栈顶
9 istore 5 将操作数栈栈顶的值保存到5slot

以上指令与如下 java 代码中的 int i = 0对应

for (int i = 0; i < n2; ++i) {
int n3 = arrn2[i];
n += n3;
}

行号 字节码指令 简要的解释
11 iload 5 5slotint值(也就是i的值)加载到操作数栈栈顶
13 iload 4 4slotint值(也就是n2的值)加载到操作数栈栈顶
15 if_icmpge 35 如果栈顶的int值大于或等于??则跳转到行号为 35 的地方
18 aload_3 3slot中引用值加载到操作数栈栈顶
19 iload 5 5slotint值加载到操作数栈栈顶
21 iaload
22 istore 6 将栈顶的int值保存到6slot
24 iload_2 2slotint值加载到操作数栈栈顶
25 iload 6 6slotint值加载到操作数栈栈顶
27 iadd 将栈顶的两个元素弹出, 再将这两个元素的和保存到栈顶
28 istore_2 将栈顶的int值保存至2slot
29 iinc 5, 1 5slot中的int值增加1
32 goto 11 跳转到行号为11的字节码指令那里
35 iload_2 2slotint值加载到操作数栈栈顶
36 ireturn 将栈顶的int值返回
  剩余部分尚未写完

参考文章

  1. 14.14.2. The enhanced for statement : https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.14.2
  2. How does the Java 'for each' loop work? 以及该问题下的一个回答
    https://stackoverflow.com/a/85206

你可能感兴趣的:(Java 语法糖(三): 对数组进行 for each 操作)