码迷,mamicode.com
首页 > 编程语言 > 详细

java foreach实现原理

时间:2014-09-24 11:08:06      阅读:241      评论:0      收藏:0      [点我收藏+]

标签:style   blog   color   io   使用   java   ar   for   div   

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实现原理

标签:style   blog   color   io   使用   java   ar   for   div   

原文地址:http://www.cnblogs.com/LuLei1990/p/3989359.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!