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就是編譯器爲咱們生成的一個迭代器變量),這邊就是給這個迭代器賦值
核心實現就是以上幾行字節碼,其他的部分就是和接下來的迭代器遍歷有關了,有興趣的朋友能夠看看.