Java foreach语法糖探秘

原创 2016年06月01日 14:36:58

    前不久线上用foreach循环,由于没判空,导致线上报了很多空指针异常。这两天梳理下foreach语法糖,看看jvm底层到底如何实现的。

    先写一个foreach的demo,包含对list对象和数组对象的遍历。

package com.jd.lvsheng;

import java.util.ArrayList;
import java.util.List;

public class Main {

	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		for (String s : list) {
			System.out.println(s);
		}

		String[] arr = new String[3];
		for (String s : arr) {
			System.out.println(s);
		}
	}
}

    然后对生成的class文件执行javap命令:javap -c Main,于是可以看这个文件的字节码了。

Compiled from "Main.java"
public class com.jd.lvsheng.Main extends java.lang.Object{
public com.jd.lvsheng.Main();
  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:   invokeinterface #4,  1; //InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
   14:  astore_2
   15:  aload_2
   16:  invokeinterface #5,  1; //InterfaceMethod java/util/Iterator.hasNext:()Z
   21:  ifeq    44
   24:  aload_2
   25:  invokeinterface #6,  1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
   30:  checkcast       #7; //class java/lang/String
   33:  astore_3
   34:  getstatic       #8; //Field java/lang/System.out:Ljava/io/PrintStream;
   37:  aload_3
   38:  invokevirtual   #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   41:  goto    15
   44:  iconst_3
   45:  anewarray       #7; //class java/lang/String
   48:  astore_2
   49:  aload_2
   50:  astore_3
   51:  aload_3
   52:  arraylength
   53:  istore  4
   55:  iconst_0
   56:  istore  5
   58:  iload   5
   60:  iload   4
   62:  if_icmpge       85
   65:  aload_3
   66:  iload   5
   68:  aaload
   69:  astore  6
   71:  getstatic       #8; //Field java/lang/System.out:Ljava/io/PrintStream;
   74:  aload   6
   76:  invokevirtual   #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   79:  iinc    5, 1
   82:  goto    58
   85:  return

}

    从第9,16,25行可以看出来,foreach在遍历List对象的时候,实则试用iterator迭代器来进行循环遍历的。循环的过程体现在21,41行。21行的ifeg命令会拿栈顶值和0比较,如果相等,则跳到44行,结束这个for循环。如果不等,会顺序执行到44行,goto 15, 也就是跳回15行。这个很像汇编语言for循环的实现。这段代码的实际等效java代码如下:

		Iterator<String> iter = list.iterator();
		while (iter.hasNext()) {
			System.out.println(iter.next());
		}


    再来看数组,数组的实现机制跟List完全不一样!因为数组并没有实现Iterator接口。

    数组是先获取数组对象的长度,然后根据这个长度来遍历的。52行的指令是arraylength,用于获得数组的长度值并压入栈顶。而62和82行构成了一个循环。if_icmpge会比较栈顶两int型数值大小,当结果大于等于0时跳转到85行。79行iinc会将第5个变量自增1.这不就是一个for循环吗!这段字节码的实际等效java代码如下:

		int len = arr.length;
		for (int i = 0; i < len; i++) {
			System.out.println(arr[i]);
		}

    分析到这里,心里就清楚了,要用foreach循环,必须保证要遍历的对象非空。语法题虽然很甜,使用方便,但是不能觉解其底层实现,用起来心惊胆战的。。。

    只有知其然且知其所以然,才能写出鲁棒性(Robustness)更好的代码。


版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

Java语法糖之foreach

语法糖是一种几乎每种语言或多或少都提供过的一些方便程序员开发代码的语法,它只是编译器实现的一些小把戏罢了,编译期间以特定的字节码或者特定的方式对这些语法做一些处理,开发者就可以直接方便地使用了。这些语...

Java中foreach循环的实现原理

Java中foreach循环的实现原理 import java.util.ArrayList; import java.util.Iterator; import java.util.List;/**...

foreach适合在什么情况下使用+foreach的内部原理剖析

 先看下面这段代码;foreach是可以遍历的。没有问题! 1 ArrayList arrList1 = new ArrayList() { 20, 3, 49, 39, 48 }; 2 ...

JAVA foreach和普通for循环是否需要判断为null

public static void main(String[] args) { List list = null; for(Object s : list){ System.out.p...
  • rnZuoZuo
  • rnZuoZuo
  • 2014年05月06日 13:12
  • 16742

JAVA foreach和普通for循环是否需要判断为null

测试增强for循环是否需要判断为null 例1: public static void main(String[] args) { List list = null; for(...

使用 Java foreach 遇到的问题

本来以为使用foreach就和使用for循环是一样的效果,结果,却遇到了意料之外的事情。先来看一个例子:...
  • lintax
  • lintax
  • 2017年02月11日 16:44
  • 590

foreach 空指针(jdk1.7)

最近碰到了个问题,发现foreach里面并没有为我们判断引用对象是否为空。所以测试了一下。 以下为测试代码: package test; import java.util.List; pu...

8张图让你轻轻松松理解Java的各种特性

这篇文章我是在其它网站看到的,感觉写的很好,图片也描述的比较形象,根据图能够非常轻松的理解JAVA的异常、集合、同步、堆栈、JVM等各种特性。 下面是转载的文章内容: 一图胜千言,下面图解均来自P...

中文字符编码之GBK,UTF-16和UTF-8

编程中经常会遇到这三种字符编码形式的相互转换问题,以至于许多第三方的库不明原因的调用失败,其实很多都是由于第三方库支持的是utf-8而不是windows默认支持的utf-16导致的。 下面介绍一下w...

Java方法传参和可变参数的个别说明,以及foreach的语法和编译后的实现

Java方法传参和可变参数的个别说明,以及foreach的语法和编译后的实现
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Java foreach语法糖探秘
举报原因:
原因补充:

(最多只允许输入30个字)