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中还有泛型、装箱、拆箱、变长字符串等)。接口
在平时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就是编译器为咱们生成的一个迭代器变量),这边就是给这个迭代器赋值
核心实现就是以上几行字节码,其他的部分就是和接下来的迭代器遍历有关了,有兴趣的朋友能够看看.