Java JDK1.5的新特性之for-each循环的底层原理

今天给大家分享一下for-each 循环的底层原理,故事还得从字节码反汇编和反编译说起...

首先看一下Java JDK1.5 的新特性

  • 泛型: ...
  • for-each. ...
  • 自动拆箱和装箱功能 ...
  • 枚举: ...
  • 可变参数 ...
  • 静态导入 ...
  • 线程并发库

对于字节码反汇编,可以使用 JDK 自带的工具 javap
首先看一下 javap 有哪些指令以及用法

$ javap
用法: javap <options> <classes>
其中, 可能的选项包括:
  -help  --help  -?        输出此用法消息
  -version                 版本信息
  -v  -verbose             输出附加信息
  -l                       输出行号和本地变量表
  -public                  仅显示公共类和成员
  -protected               显示受保护的/公共类和成员
  -package                 显示程序包/受保护的/公共类
                           和成员 (默认)
  -p  -private             显示所有类和成员
  -c                       对代码进行反汇编
  -s                       输出内部类型签名
  -sysinfo                 显示正在处理的类的
                           系统信息 (路径, 大小, 日期, MD5 散列)
  -constants               显示最终常量
  -classpath <path>        指定查找用户类文件的位置
  -cp <path>               指定查找用户类文件的位置
  -bootclasspath <path>    覆盖引导类文件的位置

  

由此可见,在我们拿到字节码文件之后,使用javap -c 字节码文件位置,即可进行反汇编

其实 for-eachjdk 1.5 的语法糖,它可以迭代集合和数组

1.for-each 迭代集合
首先来看 for-each 迭代集合

public static void main(String[] args) {
	List<String> a = new ArrayList<>();
	a.add("1");
	a.add("2");
	a.add("3");

	for (String temp : a) {
		System.out.print(temp);
	}
}

  

javap 反汇编这段代码

$ javap -c ForeachTest.class
Compiled from "ForeachTest.java"
public class cn.ixan.design.ForeachTest {
  public cn.ixan.design.ForeachTest();
    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 1
      11: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      16: pop
      17: aload_1
      18: ldc           #6                  // String 2
      20: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      25: pop
      26: aload_1
      27: ldc           #7                  // String 3
      29: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      34: pop
      35: aload_1
      36: invokeinterface #8,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
      41: astore_2
      42: aload_2
      43: invokeinterface #9,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
      48: ifeq          71
      51: aload_2
      52: invokeinterface #10,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
      57: checkcast     #11                 // class java/lang/String
      60: astore_3
      61: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
      64: aload_3
      65: invokevirtual #13                 // Method java/io/PrintStream.print:(Ljava/lang/String;)V
      68: goto          42
      71: return
}

  

由第 36 行,第 43 行,第 52 行可知,迭代集合使用到了 Iterator

然后使用 idea 查看字节码文件,发现 foreach 遍历集合运用迭代器

public static void main(String[] args) {
	List<String> a = new ArrayList();
	a.add("1");
	a.add("2");
	a.add("3");
	Iterator var2 = a.iterator();

	while(var2.hasNext()) {
		String temp = (String)var2.next();
		System.out.print(temp);
	}

}

  

2.for-each 迭代数组
我们知道集合继承 Iterable 接口,使用使用 iterator 迭代集合,
那么数组没有继承 Iterable 接口,for-each 是如何迭代它呢?

首先来看 for-each 迭代数组

public static void main(String[] args) {
	String[] arr = {"1","2"};
	for(String e : arr){
		System.out.println(e);
	}
}

  

javap 反汇编这段代码

$ javap -c ForeachTest.class
Compiled from "ForeachTest.java"
public class cn.ixan.design.ForeachTest {
  public cn.ixan.design.ForeachTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_2
       1: anewarray     #2                  // class java/lang/String
       4: dup
       5: iconst_0
       6: ldc           #3                  // String 1
       8: aastore
       9: dup
      10: iconst_1
      11: ldc           #4                  // String 2
      13: aastore
      14: astore_1
      15: aload_1
      16: astore_2
      17: aload_2
      18: arraylength
      19: istore_3
      20: iconst_0
      21: istore        4
      23: iload         4
      25: iload_3
      26: if_icmpge     49
      29: aload_2
      30: iload         4
      32: aaload
      33: astore        5
      35: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      38: aload         5
      40: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      43: iinc          4, 1
      46: goto          23
      49: return
}

  

和迭代集合不同,没有出现 Iterator,说明没有使用迭代器
第 18 行 arraylength,可能使用到了数组的 length 属性

使用 idea 反编译查看,发现 for-each 遍历数组是经典 for 循环

public static void main(String[] args) {
	String[] arr = new String[]{"1", "2"};
	String[] var2 = arr;
	int var3 = arr.length;

	for(int var4 = 0; var4 < var3; ++var4) {
		String e = var2[var4];
		System.out.println(e);
	}

}

  

总结:  1.for-each 迭代集合时,使用的是迭代器迭代集合。
     2.for-each 迭代数组时,使用的是经典for循环。

 

 

posted @ 2020-02-10 11:50  crazy-zz5536  阅读(610)  评论(0编辑  收藏  举报