• 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循环。

     

  • 相关阅读:
    UI自动化定位控件添加醒目标识
    Web自动化测试代码优化方案之定位、操作
    面试常用英文关键字知多少
    Linux常用命令必知必会
    Jenkins自动化UI测试Tomcat报错“The web application [/jenkins] appears to have started a thread named”解决办法
    appium API元素定位方法汇总
    Jmeter参数化方法
    python学习-文件读写整理
    python学习_模块化示例
    python学习-python webdriver API(转载)
  • 原文地址:https://www.cnblogs.com/ixan/p/12290358.html
Copyright © 2020-2023  润新知