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就是編譯器爲咱們生成的一個迭代器變量),這邊就是給這個迭代器賦值

    核心實現就是以上幾行字節碼,其他的部分就是和接下來的迭代器遍歷有關了,有興趣的朋友能夠看看.

相關文章
相關標籤/搜索