1. 程式人生 > >【java】foreach是如何實現的?

【java】foreach是如何實現的?

1.正文

因為想要了解編譯器是如何實現foreach功能的,就先寫一個foreach迴圈,看看位元組碼長啥樣。

public class ForEach {

	List<String> list;
	
	public void display1(){
		for(String s : list){
			System.out.println(s);
		}
	}

}

位元組碼就長下面這個樣子:
// Method descriptor #10 ()V		/*V代表返回值是void*/
// Stack: 2, Locals: 3				/*運算元棧需要2個slot,區域性變量表需要3個slot*/
public void display1();
   0  aload_0 [this]				/*將this指標推至棧頂*/
   1  getfield ambigous.ForEach.list : java.util.List [19]	/*獲得域List物件,壓入棧頂*/
   4  invokeinterface java.util.List.iterator() : java.util.Iterator [21] [nargs: 1]	/*呼叫interface的iterator方法獲得iterator物件*/
   9  astore_2						/*將其存到區域性變量表的第三個slot中(此時第一個是this,第二個空)*/
  10  goto 30						/*跳轉*/
  13  aload_2						/*將iterator物件推到棧頂*/
  14  invokeinterface java.util.Iterator.next() : java.lang.Object [27] [nargs: 1]	/*呼叫iterator的next方法*/
  19  checkcast java.lang.String [33]/*checkcast型別安全檢查*/
  22  astore_1 [s]					/*將s存到第二塊slot*/
  23  getstatic java.lang.System.out : java.io.PrintStream [35]	/*獲取靜態System.out物件*/
  26  aload_1 [s]					/*將s推到棧頂*/
  27  invokevirtual java.io.PrintStream.println(java.lang.String) : void [41]	/*呼叫out.println方法*/
  30  aload_2						/*將iterator物件推到棧頂*/
  31  invokeinterface java.util.Iterator.hasNext() : boolean [47] [nargs: 1]	/*呼叫iterator.hasNext方法*/
  36  ifne 13						/*如果結果非0,即true,跳轉*/
  39  return						/*返回*/

可以看出對於foreach的執行最終轉換成了對iterator的呼叫。那麼我們再對比下顯示使用iterator程式碼的位元組碼:

public class ForEach {

	List<String> list;
	
	public void display1(){
		for(String s : list){
			System.out.println(s);
		}
	}
	
	public void display2(){
		Iterator<String> it = list.iterator();
		while(it.hasNext()){
			System.out.println(it.next());
		}
	}
}
位元組碼:
  // Method descriptor #10 ()V
  // Stack: 2, Locals: 2
  public void display2();
     0  aload_0 [this]
     1  getfield ambigous.ForEach.list : java.util.List [19]
     4  invokeinterface java.util.List.iterator() : java.util.Iterator [21] [nargs: 1]
     9  astore_1 [it]
    10  goto 28
    13  getstatic java.lang.System.out : java.io.PrintStream [35]
    16  aload_1 [it]
    17  invokeinterface java.util.Iterator.next() : java.lang.Object [27] [nargs: 1]
    22  checkcast java.lang.String [33]
    25  invokevirtual java.io.PrintStream.println(java.lang.String) : void [41]
    28  aload_1 [it]
    29  invokeinterface java.util.Iterator.hasNext() : boolean [47] [nargs: 1]
    34  ifne 13
    37  return

看~是不是驚人的相似!其實並不驚人,一切都在預料之中。哈哈。

那麼為什麼此時區域性變量表只需要2個slot就夠了呢?

因為第一段程式碼中slot先後存了this指標、list中的元素即區域性變數s、iterator物件;而第二段程式碼中沒有String的臨時變數,只有this指標和iterator物件it。

那麼兩段程式碼棧為什麼都用了兩個slot呢?

因為其實第一段程式碼在23-26的時候棧才存滿,棧底是out,棧頂是s。其他時候棧都未滿。第二段在13-16的時候棧滿,棧底依舊out,棧頂是iterator物件it。

2.References

java forEach實現原理

http://blog.csdn.net/a596620989/article/details/6930479