Java语法糖2:foreach循环

增强for循环与普通for循环相比,功能更强并且代码更简洁

写一段代码:

    @Test
    public void test_foreach() {
        List<String> list = new ArrayList<String>();
        list.add("1111");
        list.add("2222");
        for (String str : list) {
            System.out.println(str);
        }
    }

其实我之前用jdk6时,直接在class文件就可以看到,foreach已经转化成迭代器Iterator实现,jdk8貌似不行,只能看编译后字节码文件,javap -c Test.class:

  public void test_foreach();
    Code:
       0: new           #46                 // class java/util/ArrayList
       3: dup
       4: invokespecial #47                 // Method java/util/ArrayList."<init
>":()V
       7: astore_1
       8: aload_1
       9: ldc           #48                 // String 1111
      11: invokeinterface #49,  2           // InterfaceMethod java/util/List.ad
d:(Ljava/lang/Object;)Z
      16: pop
      17: aload_1
      18: ldc           #50                 // String 2222
      20: invokeinterface #49,  2           // InterfaceMethod java/util/List.ad
d:(Ljava/lang/Object;)Z
      25: pop
      26: aload_1
      27: invokeinterface #51,  1           // InterfaceMethod java/util/List.it
erator:()Ljava/util/Iterator;
      32: astore_2
      33: aload_2
      34: invokeinterface #52,  1           // InterfaceMethod java/util/Iterato
r.hasNext:()Z
      39: ifeq          62
      42: aload_2
      43: invokeinterface #53,  1           // InterfaceMethod java/util/Iterato
r.next:()Ljava/lang/Object;
      48: checkcast     #40                 // class java/lang/String
      51: astore_3
      52: getstatic     #12                 // Field java/lang/System.out:Ljava/
io/PrintStream;
      55: aload_3
      56: invokevirtual #14                 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
      59: goto          33
      62: return

new、dup、invokespecial这些字节码指令表内定义的指令,用来执行指定c++代码,我们看不懂没关系,先暂不关注。但是我们可以看到 List.iterator(),Iterator.hasNext()这些信息,这表示foreach底层是通过迭代器实现的,实现迭代器的条件是要实现迭代器接口Iterator,很显然List继承Collection接口,Collection接口又继承Iterator接口。

但是数组也是可以使用foreach循环,但它却没有继承迭代器Iterator接口,这是怎么回事?

    @Test
    public void test_foreach_arr() {
        String[] ss = {"111","222"};
        for (String str : ss) {
            System.out.println(str);
        }
    }

看下字节码文件:

  public void test_foreach_arr();
    Code:
       0: iconst_2
       1: anewarray     #40                 // class java/lang/String
       4: dup
       5: iconst_0
       6: ldc           #41                 // String 111
       8: aastore
       9: dup
      10: iconst_1
      11: ldc           #42                 // String 222
      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     #12                 // Field java/lang/System.out:Ljava/
io/PrintStream;
      38: aload         5
      40: invokevirtual #14                 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
      43: iinc          4, 1
      46: goto          23
      49: return

其实说真的这个字节码我真看不懂,网上有人说看goto语句,貌似数组的foreach循环实际用的还是for循环,那我们写个for循环比对下它俩字节码。

数组for循环实现:

    @Test
    public void test_for() {
        String[] ss = {"111", "222"};
        for (int i = 0; i < ss.length; i++) {
            System.out.println(ss[i]);
        }
    }

字节码文件:

  public void test_for();
    Code:
       0: iconst_2
       1: anewarray     #40                 // class java/lang/String
       4: dup
       5: iconst_0
       6: ldc           #41                 // String 111
       8: aastore
       9: dup
      10: iconst_1
      11: ldc           #42                 // String 222
      13: aastore
      14: astore_1
      15: iconst_0
      16: istore_2
      17: iload_2
      18: aload_1
      19: arraylength
      20: if_icmpge     38
      23: getstatic     #12                 // Field java/lang/System.out:Ljava/
io/PrintStream;
      26: aload_1
      27: iload_2
      28: aaload
      29: invokevirtual #14                 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
      32: iinc          2, 1
      35: goto          17
      38: return

比对发现,其实数组的foreach循环和for循环运行是一致的,再回头看字节码中有个arraylength,基本可以联想到,最终数组foreach也是通过数组的长度来进行遍历。

时间: 2024-10-13 07:33:26

Java语法糖2:foreach循环的相关文章

Java语法糖1:可变长度参数以及foreach循环原理

语法糖 接下来几篇文章要开启一个Java语法糖系列,所以首先讲讲什么是语法糖.语法糖是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的字节码或者特定的方式对这些语法做一些处理,开发者就可以直接方便地使用了.这些语法糖虽然不会提供实质性的功能改进,但是它们或能提高性能.或能提升语法的严谨性.或能减少编码出错的机会.Java提供给了用户大量的语法糖,比如泛型.自动装箱.自动拆箱.foreach循环.变长参数.内部类.枚举类.断言(as

java语法糖

语法糖 Java语法糖系列,所以首先讲讲什么是语法糖.语法糖是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的字节码或者特定的方式对这些语法做一些处理,开发者就可以直接方便地使用了.这些语法糖虽然不会提供实质性的功能改进,但是它们或能提高性能.或能提升语法的严谨性.或能减少编码出错的机会.Java提供给了用户大量的语法糖,比如泛型.自动装箱.自动拆箱.foreach循环.变长参数.内部类.枚举类.断言(assert)等 断言(as

Java语法糖设计

语法糖 Java语法糖系列,所以首先讲讲什么是语法糖.语法糖是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的字节码或者特定的方式对这些语法做一些处理,开发者就可以直接方便地使用了.这些语法糖虽然不会提供实质性的功能改进,但是它们或能提高性能.或能提升语法的严谨性.或能减少编码出错的机会.Java提供给了用户大量的语法糖,比如泛型.自动装箱.自动拆箱.foreach循环.变长参数.内部类.枚举类.断言(assert)等 断言(as

Java语法糖(3):泛型

泛型初探 在泛型(Generic type或Generics)出现之前,是这么写代码的: public static void main(String[] args){List list = new ArrayList();list.add("123");list.add("456"); System.out.println((String)list.get(0));}当然这是完全允许的,因为List里面的内容是Object类型的,自然任何对象类型都可以放入.都可以

深入理解java虚拟机(十二) Java 语法糖背后的真相

语法糖(Syntactic Sugar),也叫糖衣语法,是英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语.指的是,在计算机语言中添加某种语法,这些语法糖虽然不会对语言的功能产生任何影响,却能使程序员更方便的使用语言开发程序,同时增强程序代码的可读性,避免出错的机会.但是如果只是大量添加和使用语法糖,却不去了解他,容易产生过度依赖,从而无法看清语法糖的糖衣背后,程序代码的真实面目. 总而言之,语法糖可以看做是编译器实现的一些"小把戏",这些"小

从jvm角度来解析java语法糖

java有很多语法糖,比如自动拆箱,自动装箱,foreach等等,这些原理相信每一个入门教程里都有讲,但是我相信不是每一个人 都通过查看这些语法糖的字节码来确认这些原理,因为我也是现在才想看一下. 1.自动拆箱和自动装箱 public void test() { Integer integer = 1; int i = integer; } //将常量1放入操作数栈 0: iconst_1 //调用Integer.valueOf 入参为0操作指令压入的1 1: invokestatic #2 /

Java语法糖初探(三)--变长参数

变长参数概念 在Java5 中提供了变长参数(varargs),也就是在方法定义中可以使用个数不确定的参数,对于同一方法可以使用不同个数的参数调用.形如 function(T -args).但是需要明确的一点是,java方法的变长参数只是语法糖,其本质上还是将变长的实际参数 varargs 包装为一个数组. 看下面的例子: 12345678910111213 public class VariVargs { public static void main(String []args) { tes

Java语法糖1:可变长度参数

先抄一段定义: 语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家 Peter.J.Landin 发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用.Java 中最常用的语法糖主要有泛型.变长参数.条件编译.自动拆装箱.内部类等.虚拟机并不支持这些语法,它们在编译阶段就被还原回了简单的基础语法结构,这个过程成为解语法糖. 简而言之就是语法糖就是为了方便编程,但不影响语言本身功能的情况下做的一种语法处理.虽然没有对语言本身有

Java语法糖4:内部类

内部类 最后一个语法糖,讲讲内部类,内部类指的就是在一个类的内部再定义一个类. 内部类之所以也是语法糖,是因为它仅仅是一个编译时的概念,outer.java里面定义了一个内部类inner,一旦编译成功,就会生成两个完全不同的.class文件了,分别是outer.class和outer$inner.class.所以内部类的名字完全可以和它的外部类名字相同. 内部类分为四种:成员内部类.局部内部类.匿名内部类.静态内部类.先逐一了解下,再看下使用内部类有什么好处. 成员内部类 成员内部类是最常见的内