java foreach 循环原理

java foreach 语法是在jdk1.5时加入的新特性,主要是看成for语法的一个加强,那么它的底层究竟是怎么实现的呢?由于面试时被问到,因此在这边作一个记录。java

首先来看看foreach可以使用的类型是什么:面试

 

编译器报错缘由写的很清楚了,要求:数组或java.lang.Iterable。小程序

看下jdk源码对Iterable接口的声明:设计模式

复制代码

/** Implementing this interface allows an object to be the target of
 *  the "foreach" statement.
 * @since 1.5
 */
public interface Iterable<T> {

    /**
     * Returns an iterator over a set of elements of type T.
     * 
     * @return an Iterator.
     */
    Iterator<T> iterator();
}

复制代码

实现此接口,容许成为foreach语法的target。数组

那数组呢?数组没有实现为何也能够用呢?this

那是由于遍历数组时,会转换为对数组中的每个元素的循环引用,至关于for语法循环遍历同样。设计

 

那么为何是数组或者实现了这个接口,就能实现遍历呢?实际上是由于编译器的缘由,在编译中的语义分析过程当中,有一个解除语法糖的操做,(语法糖是啥?能够理解成编译器为方便开发人员开发,会对特定代码作一些特殊处理,方便开发人员使用,除了foreach,java中还有泛型、装箱、拆箱、变长字符串等)。接口

  • 对于list编译器会调用Iterable接口的 iterator方法来循环遍历数组的元素,iterator方法中是调用Iterator接口的的 next()和hasNext()方法来作循环遍历。java中有一个叫作迭代器模式的设计模式,这个其实就是对迭代器模式的一个实现。
  • 对于数组,就是转化为对数组中的每个元素的循环引用。
  •  在平时Java程序中,应用比较多的就是对Collection集合类的foreach遍历,foreach之因此能工做,是由于这些集合类都实现了Iterable接口,该接口中定义了Iterator迭代器的ip

    产生方法,而且foreach就是经过Iterable接口在序列中进行移动。element

     Iterable接口API:

    复制代码

    package java.lang;
    
    import java.util.Iterator;
    
    public interface Iterable
    {
    
        public abstract Iterator iterator();
    }

    复制代码

    该接口中定义了产生Iterator迭代器的方法;

    复制代码

    package java.util;
    
    
    public interface Iterator
    {
    
        public abstract boolean hasNext();
    
        public abstract Object next();
    
        public abstract void remove();
    }

    复制代码

    那么为何实现了Iterable接口就支持foreach操做了呢?

    咱们写段foreach小程序看看字节码是怎么样的吧.

    复制代码

    package test;
    
    import java.util.List;
    
    /**
     * Created by vino on 2016/5/6.
     */
    public class TestForeach {
        List<Integer> integers;
        public void testForeach(){
            for(Integer i : integers){
    
            }
        }
    }

    复制代码

    其中TestForeach方法的详细字节码以下:

    复制代码

    public void testForeach();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=1, locals=3, args_size=1
             0: aload_0
             1: getfield      #2                  // Field integers:Ljava/util/List;
             4: invokeinterface #3,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
             9: astore_1
            10: aload_1
            11: invokeinterface #4,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
            16: ifeq          32
            19: aload_1
            20: invokeinterface #5,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
            25: checkcast     #6                  // class java/lang/Integer
            28: astore_2
            29: goto          10
            32: return
          LineNumberTable:
            line 11: 0
            line 13: 29
            line 14: 32
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
               29       0     2     i   Ljava/lang/Integer;
                0      33     0  this   Ltest/TestForeach;
    }

    复制代码

     咱们重点看红色部分,我来简单解释一下这段字节码的做用:

    0:加载this到操做栈

    1:获取字段integers

    4:调用integers的接口方法interator

    9:将返回的迭代器赋给本地变量?(咱们看到在最下面的本地变量区的Slot列,有0和2,可是没有1.事实上,1就是编译器为咱们生成的一个迭代器变量),这边就是给这个迭代器赋值

    核心实现就是以上几行字节码,其他的部分就是和接下来的迭代器遍历有关了,有兴趣的朋友能够看看.