关于for循环的再次理解

先来看一段代码

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是最简单直接的理解方式。

原文发布于微信公众号 - Android每日一讲(gh_f053f29083b9)

原文发表时间:2018-05-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏微信公众号:Java团长

Java String 对 null 对象的容错处理

第一句相信大家都会容易理解,这是类型初始化的基础知识,但是第二句就让我很疑惑:为什么打印一个 null 对象不会抛出异常?带着这个疑问,我开始了解惑之旅。下面我...

992
来自专栏木子昭的博客

正则 (入门篇)简单来说写好正则表达式的两个要点:写在最后

如果你对正则感兴趣,读完这篇文章,一定会有收获~_^ 简单来说 正则一般代指正则表达式 正则表达式是从"复杂数据"中抽取"有用数据"的公式 ---- 写好正则...

3078
来自专栏和蔼的张星的图像处理专栏

212. 空格替换先扩充,从后往前处理

设计一种方法,将一个字符串中的所有空格替换成 %20 。你可以假设该字符串有足够的空间来加入新的字符,且你得到的是“真实的”字符长度。 你的程序还需要返回被替...

882
来自专栏司想君

JavaScript闭包,只学这篇就会了

昨天发的文章,排版出现了重大失误。让大家的眼睛受累了。今天再发一遍。 这篇文章使用一些简单的代码例子来解释JavaScript闭包的概念,即使新手也可以轻松参透...

2838
来自专栏xingoo, 一个梦想做发明家的程序员

Java程序员的日常——《编程思想》一切都是对象

今天终于看完了第一章,哈哈,万事开头难....刚开始被编程思想的第一章给蒙住了,讲一堆理论,没什么意思。从第二章开始,真正的开始讲解Java相关的内容,有了一...

19610
来自专栏诸葛青云的专栏

C语言入门基础学习函数?来看我就告诉你!

在前面我们已经讲过了一些简单的函数,如程序的主函数main()、标准输出函数printf()。在C语言中,大多数功能都是依靠函数来实现的。But,你知道什么是函...

1333
来自专栏程序猿DD

第五章 正则表达式的拆分【修订】

本篇文章本不该存在,因小编的失误出现了一些错误,应作者要求,修正昨天同名文章的两处错误。 第五章 正则表达式的拆分 对于一门语言的掌握程度怎么样,可以有两个角度...

2026
来自专栏达摩兵的技术空间

a>b的那些事

经常会遇到对比两个值大小关系的逻辑,常规的处理中我们都是处理两个数字或者数字类型的字符串。那么这里进行延伸拓展的练习,来避免一些开发中的采坑。

701
来自专栏布尔

想起温习一下JS中的this apply call arguments

很多时候讲到语言入门,大家会认为就是要了解一下语言的语法、数据类型和常用函数。这一课对于所有的计算机专业的毕业生来说都可以自学,然而在最近的实践中(带了两个实习...

22110
来自专栏微信公众号:Java团长

Java反射机制详解

反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象...

952

扫码关注云+社区

领取腾讯云代金券