Java for-each 原理相關解析
阿新 • • 發佈:2018-12-11
要說明其原理可以先看下下面的基本用法:
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("111");
list.add("222");
for (String str : list) {
System.out.println(str);
}
}
老規矩,上面既然是 foreach 用法,那就編譯成 class 然後用 javap 看下位元組碼咯,如下:
javap -c Test. class
Compiled from "Test.java"
public class Test {
public Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String 111
11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
16 : pop
17: aload_1
18: ldc #6 // String 222
20: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
25: pop
26: aload_1
27: invokeinterface #7, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
32: astore_2
33: aload_2
34: invokeinterface #8, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
39: ifeq 62
42: aload_2
43: invokeinterface #9, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
48: checkcast #10 // class java/lang/String
51: astore_3
52: getstatic #11 // Field java/lang/System.out:Ljava/io/PrintStream;
55: aload_3
56: invokevirtual #12 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
59: goto 33
62: return
}
通過上面位元組碼很明顯可以發現,在編譯的時候編譯器會自動將對 for 這個關鍵字的使用轉化為對目標的迭代器使用,這就是 foreach 迴圈的原理。所以 ArrayList 之所以能使用 foreach 迴圈遍歷,是因為 ArrayList 的父類 AbstractList 正確地實現了 Iterable 介面的 iterator 方法。所以有時候當我們自己實現一個 ArrayList 且用 foreach 迴圈時會發現報空指標異常,原因就是因為自己寫的 ArrayList 沒有實現 Iterable 介面。因為 java 中任何一個集合,無論是 JDK 提供的還是自己寫的,只要想使用 foreach 迴圈遍歷就必須正確地實現 Iterable 介面,這也是迭代器模式的體現。