Java语法糖之foreach

JAVA集合foreach

for-each其实只是java提供的语法糖。语法糖是编程语言提供的一些便于程序员书写代码的语法,是编译器提供给程序员的糖衣,编译时会对这些语法特殊处理。语法糖虽然不会带来实质性的改进,但是在提高代码可读性,提高语法严谨性,减少编码错误机会上确实做出了很大贡献;
Java要求集合必须实现Iterable接口,才能使用for-each语法糖遍历该集合的实例;
先看个简单的例子:

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(12, 30, 88);
        for (Integer num : list) {
            System.err.println(num);
        }
    }

使用javap -v 反编译

Classfile /Users/Robert/gitlab/learn/target/classes/com/quancheng/SockerServer.class
  Last modified 2018-9-29; size 1126 bytes
  MD5 checksum d6c68f72bdc08b156eda0315c1bdb0c9
  Compiled from "SockerServer.java"
public class com.quancheng.SockerServer
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #11.#34        // java/lang/Object."<init>":()V
   #2 = Class              #35            // java/lang/Integer
   #3 = Methodref          #2.#36         // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   #4 = Methodref          #37.#38        // java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
   #5 = InterfaceMethodref #39.#40        // java/util/List.iterator:()Ljava/util/Iterator;
   #6 = InterfaceMethodref #41.#42        // java/util/Iterator.hasNext:()Z
   #7 = InterfaceMethodref #41.#43        // java/util/Iterator.next:()Ljava/lang/Object;
   #8 = Fieldref           #44.#45        // java/lang/System.err:Ljava/io/PrintStream;
   #9 = Methodref          #46.#47        // java/io/PrintStream.println:(Ljava/lang/Object;)V
  #10 = Class              #48            // com/quancheng/SockerServer
  #11 = Class              #49            // java/lang/Object
  #12 = Utf8               <init>
  #13 = Utf8               ()V
  #14 = Utf8               Code
  #15 = Utf8               LineNumberTable
  #16 = Utf8               LocalVariableTable
  #17 = Utf8               this
  #18 = Utf8               Lcom/quancheng/SockerServer;
  #19 = Utf8               main
  #20 = Utf8               ([Ljava/lang/String;)V
  #21 = Utf8               num
  #22 = Utf8               Ljava/lang/Integer;
  #23 = Utf8               args
  #24 = Utf8               [Ljava/lang/String;
  #25 = Utf8               list
  #26 = Utf8               Ljava/util/List;
  #27 = Utf8               LocalVariableTypeTable
  #28 = Utf8               Ljava/util/List<Ljava/lang/Integer;>;
  #29 = Utf8               StackMapTable
  #30 = Class              #50            // java/util/List
  #31 = Class              #51            // java/util/Iterator
  #32 = Utf8               SourceFile
  #33 = Utf8               SockerServer.java
  #34 = NameAndType        #12:#13        // "<init>":()V
  #35 = Utf8               java/lang/Integer
  #36 = NameAndType        #52:#53        // valueOf:(I)Ljava/lang/Integer;
  #37 = Class              #54            // java/util/Arrays
  #38 = NameAndType        #55:#56        // asList:([Ljava/lang/Object;)Ljava/util/List;
  #39 = Class              #50            // java/util/List
  #40 = NameAndType        #57:#58        // iterator:()Ljava/util/Iterator;
  #41 = Class              #51            // java/util/Iterator
  #42 = NameAndType        #59:#60        // hasNext:()Z
  #43 = NameAndType        #61:#62        // next:()Ljava/lang/Object;
  #44 = Class              #63            // java/lang/System
  #45 = NameAndType        #64:#65        // err:Ljava/io/PrintStream;
  #46 = Class              #66            // java/io/PrintStream
  #47 = NameAndType        #67:#68        // println:(Ljava/lang/Object;)V
  #48 = Utf8               com/quancheng/SockerServer
  #49 = Utf8               java/lang/Object
  #50 = Utf8               java/util/List
  #51 = Utf8               java/util/Iterator
  #52 = Utf8               valueOf
  #53 = Utf8               (I)Ljava/lang/Integer;
  #54 = Utf8               java/util/Arrays
  #55 = Utf8               asList
  #56 = Utf8               ([Ljava/lang/Object;)Ljava/util/List;
  #57 = Utf8               iterator
  #58 = Utf8               ()Ljava/util/Iterator;
  #59 = Utf8               hasNext
  #60 = Utf8               ()Z
  #61 = Utf8               next
  #62 = Utf8               ()Ljava/lang/Object;
  #63 = Utf8               java/lang/System
  #64 = Utf8               err
  #65 = Utf8               Ljava/io/PrintStream;
  #66 = Utf8               java/io/PrintStream
  #67 = Utf8               println
  #68 = Utf8               (Ljava/lang/Object;)V
{
  public com.quancheng.SockerServer();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 11: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/quancheng/SockerServer;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=4, args_size=1
         0: iconst_3
         1: anewarray     #2                  // class java/lang/Integer
         4: dup
         5: iconst_0
         6: bipush        12
         8: invokestatic  #3                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        11: aastore
        12: dup
        13: iconst_1
        14: bipush        30
        16: invokestatic  #3                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        19: aastore
        20: dup
        21: iconst_2
        22: bipush        88
        24: invokestatic  #3                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        27: aastore
        28: invokestatic  #4                  // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
        31: astore_1
        32: aload_1
        33: invokeinterface #5,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
        38: astore_2
        39: aload_2
        40: invokeinterface #6,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
        45: ifeq          68
        48: aload_2
        49: invokeinterface #7,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
        54: checkcast     #2                  // class java/lang/Integer
        57: astore_3
        58: getstatic     #8                  // Field java/lang/System.err:Ljava/io/PrintStream;
        61: aload_3
        62: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
        65: goto          39
        68: return
      LineNumberTable:
        line 13: 0
        line 14: 32
        line 15: 58
        line 16: 65
        line 17: 68
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           58       7     3   num   Ljava/lang/Integer;
            0      69     0  args   [Ljava/lang/String;
           32      37     1  list   Ljava/util/List;
      LocalVariableTypeTable:
        Start  Length  Slot  Name   Signature
           32      37     1  list   Ljava/util/List<Ljava/lang/Integer;>;
      StackMapTable: number_of_entries = 2
        frame_type = 253 /* append */
          offset_delta = 39
          locals = [ class java/util/List, class java/util/Iterator ]
        frame_type = 250 /* chop */
          offset_delta = 28
}

注意看这行JVM指令:

33: invokeinterface #5,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;

从上面的字节码指令可以看出foreach语法实际调用的还是List.iterator()方法然后使用迭代器进行迭代,这就是集合foreach的语法糖;

for (Integer num : list) {
    System.err.println(num);
}

由Jvm编译期处理后等效于:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
    {VariableModifier} TargetType Identifier =
        (TargetType) #i.next();
    Statement
}

java数组foreach

数组的实现机制跟List完全不一样,因为数组并没有实现Iterator接口,并没有采用Iterable实现转换。真正的解析结果如下所示:

T[] #a = Expression;
L1: L2: ... Lm:
for (int #i = 0; #i < #a.length; #i++) {
    {VariableModifier} TargetType Identifier = #a[#i];
    Statement
}

说明:javap是JDK自带的反汇编器,可以查看java编译器为我们生成的字节码

【知识点】

  • for-each遍历的集合对象不能为null
    既然对集合的for-each遍历实际上是使用迭代器,会调用集合对象的iterator()方法获得迭代器,那么,对null集合的for-each遍历,就会在null集合对象上调用方法,势必会抛出空指针异常;
  • for-each遍历时不能改变正在遍历的集合
    因为在使用迭代器遍历集合时,不能够改变集合,所以for-each遍历时改变集合,同样会引发ConcurrentModificationException异常;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值