要说明其原理可以先看下下面的基本用法:
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 接口,这也是迭代器模式的体现。