



纵观所有的字节码指令,并没有与for名字相关的指令,那for循环是如何实现的呢?接下来以sum相加求和的例子来看for循环的实现细节,代码如下所示。
public int sum(int[] numbers) {
int sum = 0;
for (int number : numbers) {
sum += number;
}
return sum;
}
上面代码对应的字节码如下。
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
为了方便理解,这里先把对应的局部变量表的示意图画出来,如图2-14所示。
图2-14 for循环局部变量表示意图
下面以numbers数组内容[10,20,30]为例来讲解上述的字节码的执行过程。
第0~1行:把常量0加载到操作数栈上,随后通过istore_2指令将0出栈赋值给局部变量表下标为2的元素,也就是给局部变量sum赋值为0,如图2-15所示。
图2-15 for循环执行细节(1)
第2~9行用来初始化循环控制变量,其伪代码如下所示。
$array = numbers; $len = $array.arraylength $i = 0
第2~3行:aload_1指令的作用是加载局部变量表中下标为1的变量(参数numbers),astore_3指令的作用是将栈顶元素存储到局部变量下标为3的位置上,记为$array,如图2-16所示。
图2-16 for循环执行细节(2)
第4~6行:计算数组的长度,astore_3加载$array到栈顶,调用arraylength指令获取数组长度存储到栈顶,随后调用istore 4将数组长度存储到局部变量表的第4个位置,这个变量是表示数组的长度值,记为$len,过程如图2-17所示。
第8~9行:初始化数组遍历的下标初始值。iconst_0将0加载到操作数栈上,随后istore 5将栈顶的0存储到局部变量表中的第5个位置,这个局部变量是数组遍历的下标初始值,记为$i,如图2-18所示。
11~32行是真正的循环体,详细介绍如下。
图2-17 for循环执行细节(3)
图2-18 for循环执行细节(4)
第11~15行的作用是判断循环能否继续。这部分的字节码如下所示。
11: iload 5 13: iload 4 15: if_icmpge 35
首先通过iload 5和iload 4指令加载$i和$len到栈顶,然后调用if_icmpge进行比较,如果$i>=$len,直接跳转到第35行指令处,for循环结束;如果$i<$len则继续往下执行循环体,可以用如下伪代码表示。
if ($i >= $len) goto 35;
过程如图2-19所示。
第18~22行的作用是把$array[$i]赋值给number。aload_3加载$array到栈上,iload 5加载$i到栈上,然后iaload指令把下标为$i的数组元素加载到操作数栈上,随后istore 6将栈顶元素存储到局部变量表下标为6的位置上,过程如图2-20所示。
图2-19 for循环执行细节(5)
图2-20 for循环执行细节(6)
第24~28行:iload_2和iload 6指令把sum和number值加载到操作数栈上,然后执行iadd指令进行整数相加,过程如图2-21所示。
第29行:“iinc 5,1”指令对执行循环后的$i加一。iinc指令比较特殊,之前介绍的指令都是基于操作数栈来实现功能,它则是直接对局部变量进行自增,不用先入栈、执行加一操作,再将结果出栈存储到局部变量表,因此效率非常高,适合循环结构,如图2-22所示。
第32行:goto 11指令的作用是跳转到第11行继续进行循环条件的判断。
图2-21 for循环执行细节(7)
图2-22 for循环执行细节(8)
上述字节码用伪代码表示就是:
@start: if ($i >= $len) return; $item = $array[$i]; sum += $item; ++ $i goto @start
整段代码的逻辑看起来非常熟悉,可以用下面的Java代码表示。
int sum = 0;
for (int i = 0; i < numbers.length; i++) {
sum += numbers[i];
}
return sum;
由此可见,for(item:array)就是一个语法糖,字节码会让它现出原形,回归它的本质。