关于for循环的再次理解

97 阅读3分钟
原文链接: mp.weixin.qq.com

先来看一段代码

import java.util.List;import java.util.Arrays;public class ForDemo {    public static void main(String[] args) {      List<String> sources = Arrays.asList("first", "second", "third");      for(String str : sources){        System.out.println(str);      }    }}

这里的 for循环写法是1.5之后引入的新 feature,允许开发者用简明的方式遍历一个数组中的元素。

这种写法很符合人类思考的方式,如果用 kotlin来写的话从语法上更贴合自然语言的思维,

for(str in sources) { ....}

翻译成自然语言的话,就是"遍历 sources 中的 str 元素"。

不过关于这种简化写法和我们从接触 java第一天开始就学习的传统 for写法有什么区别呢,不知道有深入去看过没有。

深入分析

可能很多人已经知道简化写法其实也是通过 Iterator来遍历元素的,原理很简单,不过我想用最傻的方式来分析这里面的源码实现,掌握这种方式以后可以推此至彼,遇到任何问题都能理出一个思路来。

首先我们看一下ForDemo.java编译后的源码是怎样的,

javap -c ForDemo.class

 public class ForDemo {  public ForDemo();    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_3       1: anewarray     #2                  // class java/lang/String       4: dup       5: iconst_0       6: ldc           #3                  // String first       8: aastore       9: dup      10: iconst_1      11: ldc           #4                  // String second      13: aastore      14: dup      15: iconst_2      16: ldc           #5                  // String third      18: aastore      19: invokestatic  #6                  // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;      22: astore_1      23: aload_1      24: invokeinterface #7,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;      29: astore_2      30: aload_2      31: invokeinterface #8,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z      36: ifeq          59      39: aload_2      40: invokeinterface #9,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;      45: checkcast     #2                  // class java/lang/String      48: astore_3      49: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;      52: aload_3      53: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V      56: goto          30      59: return}

重点在 #24 行开始,可以看到这里有 Iterator的方法调用。

关于 Iterator,我们知道任何继承了 Collection的类都需要默认实现一下 Iterator的接口,比如 hasNext(), next(),

从源码上来看的话,可以确定的是 for循环也是通过迭代器来实现遍历的,并且调用了 hasNext和 next方法,

确定了这一点之后就可以猜到,在源码级别的for循环会被编译器优化成下面这样

for(Iterator itr = source.iterator() ; itr.hasNext()) {    String str = itr.next();    ....}

举一反三,对于任何的Java代码都可以用 javap 的方式来看编译后的机器码,如果对于某个代码或则语法糖有疑问的话,javap是最简单直接的理解方式。