首页 > 代码库 > java foreach实现原理

java foreach实现原理

java  foreach 语法糖实现原理

 

一 、  示例代码

 

 1 import java.util.ArrayList; 2 import java.util.List; 3  4 /** 5  *  6  * @author lulei 7  * @date 2014-9-23 8  *  9  */10 public class TestForeach {11 12     private List<String> list = new ArrayList<String>();13     private String[] array = new String[10];14 15     void testCollect() {16         for (String str : list) {17             System.out.println(str);18         }19     }20 21     void testArray() {22         for (String str : array)23             System.out.println(str);24     }25 }

 

二 、  字节码

     

void testCollect();    Code:       0: aload_0                                 1: getfield      #4                  // Field list:Ljava/util/List;       4: invokeinterface #7,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;       9: astore_1            10: aload_1             11: invokeinterface #8,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z      16: ifeq          39      19: aload_1             20: invokeinterface #9,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;      25: checkcast     #5                  // class java/lang/String      28: astore_2            29: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;      32: aload_2             33: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V      36: goto          10      39: return          void testArray();    Code:       0: aload_0              1: getfield      #6                  // Field array:[Ljava/lang/String;       4: astore_1             5: aload_1              6: arraylength          7: istore_2             8: iconst_0             9: istore_3            10: iload_3             11: iload_2             12: if_icmpge     34      15: aload_1             16: iload_3             17: aaload              18: astore        4      20: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;      23: aload         4      25: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V      28: iinc          3, 1      31: goto          10      34: return

 

三、 注释

      void testCollect();

    可以看到这是在java虚拟机编译时,将集合容器类型的foreach转化成了使用迭代器进行迭代遍历, 源码中我们只是显式的声明一个局部变量(str),但是在经过编译器编译后已经使用到下标为2的slot(分别为this,iterator,str所用)。这是foreach语法糖在经过虚拟机编译时,生成的临时变量。

 

       0: aload_0                          // 将 this 从存储栈帧load到操作栈帧上       1: getfield      #4                 // 将 list 成员变量load到操作栈帧上       4: invokeinterface #7,  1           // 调用 list.iterator() 方法返回 iterator 临时迭代变量到操作栈帧上       9: astore_1                         // 将栈帧栈帧上的新返回的迭代变量的引用存储到存储栈帧的1号slot中      10: aload_1                          // 将 1 号slot中的迭代变量的引用load到操作栈帧上      11: invokeinterface #8,  1           // 调用Iterator接口声明的 hasNext() 方法,返回一个boolean(实际是 0或者1)到操作栈上      16: ifeq          39                 // 如果返回的是0,则调到程序结束      19: aload_1                          // 否则,将1号slot中的临时迭代变量load到操作栈帧上      20: invokeinterface #9,  1           // 调用临时迭代变量的 next() 方法,返回当前的 string 实例      25: checkcast     #5                 // 检查上一步返回时的引用类型是否是String类型      28: astore_2                         // 将返回的 string 对象从操作栈保存到存储栈      29: getstatic     #10                // 将 out 对象load到操作栈帧上      32: aload_2                          // 将2号slot中的那个 string 对象load到操作栈帧上      33: invokevirtual #11                // 调用 out 对象的 println 方法      36: goto          10                 // 循环继续      39: return        

 

          void testArray();

    在下面的分析中你将看到,数组的foreach语法糖在编译时,是被分解成类似for的循环(准确的是while循环),与一般的循环语句相比并没有什么新的虚拟机指令使用。

       0: aload_0                       // 将 this 从存储栈帧load到操作栈帧上       1: getfield      #6                   // 将 array 成员变量load到操作栈帧上       4: astore_1                           // 将操作栈帧中的 array存储到存储栈帧中 1 号slot里       5: aload_1                   // 将存储栈中的 array load到操作栈帧中       6: arraylength               // 计算 array 引用对应数组的长度       7: istore_2                  // 将 array 的长度保存到存储栈帧的 2 号slot里       8: iconst_0                  // 将常数 0 压入操作栈帧中       9: istore_3                  // 将操作栈帧里的常数 0 保存到存储栈帧3 号 slot里      10: iload_3                   // 将存储栈帧里 3 号slot中的常数 0 load到操作栈帧上 (数组下标计数变量)      11: iload_2                   // 将存储栈帧 2 号slot中的 array 的长度值load到操作栈帧里      12: if_icmpge     34           // 比较操作栈帧里的数组的访问下标是否大于等于数组的长度,相等则跳转程序结束      15: aload_1                           // 将存储栈帧中的 1 号slot中存储的 array 引用load到操作栈里      16: iload_3                           // 将存储栈帧中 3 号slot中存储的数组访问下标变量load到操作栈帧中      17: aaload                            // 将 array 的当前操作栈帧中对应的下标位置的String引用load到操作栈帧中       18: astore        4                   // 将操作栈帧上的String引用存储到存储栈帧的 4 号slot中      20: getstatic     #10                 // 将 out 对象load到操作栈帧上      23: aload         4                   // 将存储栈帧中 4 号slot中的String变量load到操作栈中      25: invokevirtual #11                 // 调用 out 对象的 println 方法      28: iinc          3, 1                // 将存储栈帧中 3 号 slot对应的值加1 并保存到原位置(数组下标加 1)      31: goto          10                  // 继续循环      34: return      

 

四、总结

      1,既然没有”新的实质性质的东西“,那么为什么还要使用呢?

    没有新的东西,也没有对效率向差的方向影响,但是可以发现这里的这里的循环变量(编译器编译时生成的迭代变量和数组访问下边变量)都被隐藏在循环语句的内部,这样就缩小的局部变量的作用域,如  Effective Java那本书上所提,这样可以防止变量作用域污染,如果误用出作用域的变量则在编译时就可以查出。更重要的是这样做某些情况下可以缩小函数调用栈帧中的存储栈帧的长度。当然如果现代虚拟机都有 活性分析优化,可以优化掉这部分的问题。

   2 ,注释那部分mark的那条 checkCast字节码是干什么的?

    其实这个和foreach语法糖没有任何关系,只是这里是泛型的问题,因为java泛型是使用checkCast

       

java foreach实现原理