反编译解析数组使用foreach

反编译解析数组使用foreach

一、说明

  • foreach 循环遍历的原理是通过迭代器 Iterator,不断获取 next 元素,所以使用 foreach,编译的时候编译器会自动将对 for 这个关键字的使用转化为对目标的迭代器的使用
  • 我们常常认为,要想使用 foreach 循环遍历,就必须正确地实现 Iterable 接口,但是,你有没有想过,数组没有实现 Iterable 接口,为什么可以使用 foreach 语句循环遍历呢?这里我使用几个例子,以及进行Java反编译来说明数组这个特殊情况

二、集合使用foreach

  • 首先,我们基于对照试验,使用实现 Iterable 接口的 Collection 集合(ArrayList)进行 foreach 使用
import java.util.ArrayList;

/**
 *
 * @author 吕泽江
 * @version 创建日期: 2021-3-15 14:39:11
 */
public class TestCollection {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		ArrayList<Integer> arrayList = new ArrayList<>();
		arrayList.add(1);
		arrayList.add(2);
		arrayList.add(3);
		arrayList.add(4);
		arrayList.add(5);
		
		for (Integer integer : arrayList) {
			System.out.println(integer);
		}
	}
}

三、数组使用foreach

  • 然后,我们使用没有实现 Iterable 接口的数组来进行 foreach 对比
/**
 *
 * @author 吕泽江
 * @version 创建日期: 2021-3-15 14:42:28
 */
public class TestArray {
	public static void main(String[] args) {
		int[] arr = {6,7,8,9,10};
		for (int i : arr) {
			System.out.println(i);
		}
	}
}
  • 以上两个程序十分简单,可以看到输出结果均为预期结果,但是单单这样,我们还不至于探究数组与 foreach 的特殊关系

四、数组使用for

  • 这里举例数组使用 for 的目的在于下述对于Java反编译的对照
/**
 *
 * @author 吕泽江
 * @version 创建日期: 2021-3-15 14:45:42
 */
public class TestFor {
	public static void main(String[] args) {
		int[] arr = {6,7,8,9,10};
		for (int i = 0; i < arr.length; i++) {
			System.out.println(arr[i]);
		}
	}
}

五、javap反编译程序

  • 我们使用以下命令来对上述几个程序进行Java反编译处理
javac -encoding utf-8 TestCollection.java
javap -v TestCollection.class
javac -encoding utf-8 TestArray.java
javap -v TestArray.class
javac -encoding utf-8 TestFor.java
javap -v TestFor.class

5.1 TestCollection结果

Classfile /D:/workspace/Test/src/Test0315/TestCollection.class
  Last modified 2021-3-15; size 852 bytes
  MD5 checksum abb0ab7ce240eb4590733fa75e5448a5
  Compiled from "TestCollection.java"
public class Test0315.TestCollection
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #13.#25        // java/lang/Object."<init>":()V
   #2 = Class              #26            // java/util/ArrayList
   #3 = Methodref          #2.#25         // java/util/ArrayList."<init>":()V
   #4 = Methodref          #9.#27         // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   #5 = Methodref          #2.#28         // java/util/ArrayList.add:(Ljava/lang/Object;)Z
   #6 = Methodref          #2.#29         // java/util/ArrayList.iterator:()Ljava/util/Iterator;
   #7 = InterfaceMethodref #30.#31        // java/util/Iterator.hasNext:()Z
   #8 = InterfaceMethodref #30.#32        // java/util/Iterator.next:()Ljava/lang/Object;
   #9 = Class              #33            // java/lang/Integer
  #10 = Fieldref           #34.#35        // java/lang/System.out:Ljava/io/PrintStream;
  #11 = Methodref          #36.#37        // java/io/PrintStream.println:(Ljava/lang/Object;)V
  #12 = Class              #38            // Test0315/TestCollection
  #13 = Class              #39            // java/lang/Object
  #14 = Utf8               <init>
  #15 = Utf8               ()V
  #16 = Utf8               Code
  #17 = Utf8               LineNumberTable
  #18 = Utf8               main
  #19 = Utf8               ([Ljava/lang/String;)V
  #20 = Utf8               StackMapTable
  #21 = Class              #26            // java/util/ArrayList
  #22 = Class              #40            // java/util/Iterator
  #23 = Utf8               SourceFile
  #24 = Utf8               TestCollection.java
  #25 = NameAndType        #14:#15        // "<init>":()V
  #26 = Utf8               java/util/ArrayList
  #27 = NameAndType        #41:#42        // valueOf:(I)Ljava/lang/Integer;
  #28 = NameAndType        #43:#44        // add:(Ljava/lang/Object;)Z
  #29 = NameAndType        #45:#46        // iterator:()Ljava/util/Iterator;
  #30 = Class              #40            // java/util/Iterator
  #31 = NameAndType        #47:#48        // hasNext:()Z
  #32 = NameAndType        #49:#50        // next:()Ljava/lang/Object;
  #33 = Utf8               java/lang/Integer
  #34 = Class              #51            // java/lang/System
  #35 = NameAndType        #52:#53        // out:Ljava/io/PrintStream;
  #36 = Class              #54            // java/io/PrintStream
  #37 = NameAndType        #55:#56        // println:(Ljava/lang/Object;)V
  #38 = Utf8               Test0315/TestCollection
  #39 = Utf8               java/lang/Object
  #40 = Utf8               java/util/Iterator
  #41 = Utf8               valueOf
  #42 = Utf8               (I)Ljava/lang/Integer;
  #43 = Utf8               add
  #44 = Utf8               (Ljava/lang/Object;)Z
  #45 = Utf8               iterator
  #46 = Utf8               ()Ljava/util/Iterator;
  #47 = Utf8               hasNext
  #48 = Utf8               ()Z
  #49 = Utf8               next
  #50 = Utf8               ()Ljava/lang/Object;
  #51 = Utf8               java/lang/System
  #52 = Utf8               out
  #53 = Utf8               Ljava/io/PrintStream;
  #54 = Utf8               java/io/PrintStream
  #55 = Utf8               println
  #56 = Utf8               (Ljava/lang/Object;)V
{
  public Test0315.TestCollection();
    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 13: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=1
         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: iconst_1
        10: invokestatic  #4                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        13: invokevirtual #5                  // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
        16: pop
        17: aload_1
        18: iconst_2
        19: invokestatic  #4                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        22: invokevirtual #5                  // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
        25: pop
        26: aload_1
        27: iconst_3
        28: invokestatic  #4                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        31: invokevirtual #5                  // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
        34: pop
        35: aload_1
        36: iconst_4
        37: invokestatic  #4                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        40: invokevirtual #5                  // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
        43: pop
        44: aload_1
        45: iconst_5
        46: invokestatic  #4                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
        49: invokevirtual #5                  // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
        52: pop
        53: aload_1
        54: invokevirtual #6                  // Method java/util/ArrayList.iterator:()Ljava/util/Iterator;
        57: astore_2
        58: aload_2
        59: invokeinterface #7,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
        64: ifeq          87
        67: aload_2
        68: invokeinterface #8,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
        73: checkcast     #9                  // class java/lang/Integer
        76: astore_3
        77: getstatic     #10                 // Field java/lang/System.out:Ljava/io/PrintStream;
        80: aload_3
        81: invokevirtual #11                 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
        84: goto          58
        87: return
      LineNumberTable:
        line 19: 0
        line 20: 8
        line 21: 17
        line 22: 26
        line 23: 35
        line 24: 44
        line 26: 53
        line 27: 77
        line 28: 84
        line 29: 87
      StackMapTable: number_of_entries = 2
        frame_type = 253 /* append */
          offset_delta = 58
          locals = [ class java/util/ArrayList, class java/util/Iterator ]
        frame_type = 250 /* chop */
          offset_delta = 28
}
SourceFile: "TestCollection.java"

5.2 TestArray结果

Classfile /D:/workspace/Test/src/Test0315/TestArray.class
  Last modified 2021-3-15; size 543 bytes
  MD5 checksum 0f5e743a1a6a5a08bf3ecb086eba6ed4
  Compiled from "TestArray.java"
public class Test0315.TestArray
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#17         // java/lang/Object."<init>":()V
   #2 = Fieldref           #18.#19        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #20.#21        // java/io/PrintStream.println:(I)V
   #4 = Class              #22            // Test0315/TestArray
   #5 = Class              #23            // java/lang/Object
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               main
  #11 = Utf8               ([Ljava/lang/String;)V
  #12 = Utf8               StackMapTable
  #13 = Class              #24            // "[Ljava/lang/String;"
  #14 = Class              #25            // "[I"
  #15 = Utf8               SourceFile
  #16 = Utf8               TestArray.java
  #17 = NameAndType        #6:#7          // "<init>":()V
  #18 = Class              #26            // java/lang/System
  #19 = NameAndType        #27:#28        // out:Ljava/io/PrintStream;
  #20 = Class              #29            // java/io/PrintStream
  #21 = NameAndType        #30:#31        // println:(I)V
  #22 = Utf8               Test0315/TestArray
  #23 = Utf8               java/lang/Object
  #24 = Utf8               [Ljava/lang/String;
  #25 = Utf8               [I
  #26 = Utf8               java/lang/System
  #27 = Utf8               out
  #28 = Utf8               Ljava/io/PrintStream;
  #29 = Utf8               java/io/PrintStream
  #30 = Utf8               println
  #31 = Utf8               (I)V
{
  public Test0315.TestArray();
    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

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=6, args_size=1
         0: iconst_5
         1: newarray       int
         3: dup
         4: iconst_0
         5: bipush        6
         7: iastore
         8: dup
         9: iconst_1
        10: bipush        7
        12: iastore
        13: dup
        14: iconst_2
        15: bipush        8
        17: iastore
        18: dup
        19: iconst_3
        20: bipush        9
        22: iastore
        23: dup
        24: iconst_4
        25: bipush        10
        27: iastore
        28: astore_1
        29: aload_1
        30: astore_2
        31: aload_2
        32: arraylength
        33: istore_3
        34: iconst_0
        35: istore        4
        37: iload         4
        39: iload_3
        40: if_icmpge     63
        43: aload_2
        44: iload         4
        46: iaload
        47: istore        5
        49: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        52: iload         5
        54: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        57: iinc          4, 1
        60: goto          37
        63: return
      LineNumberTable:
        line 13: 0
        line 14: 29
        line 15: 49
        line 14: 57
        line 17: 63
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 37
          locals = [ class "[Ljava/lang/String;", class "[I", class "[I", int, int ]
          stack = []
        frame_type = 248 /* chop */
          offset_delta = 25
}
SourceFile: "TestArray.java"

5.3 TestFor结果

Classfile /D:/workspace/Test/src/Test0315/TestFor.class
  Last modified 2021-3-15; size 492 bytes
  MD5 checksum 40766f0529a4d099ff82983b65b42540
  Compiled from "TestFor.java"
public class Test0315.TestFor
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#16         // java/lang/Object."<init>":()V
   #2 = Fieldref           #17.#18        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #19.#20        // java/io/PrintStream.println:(I)V
   #4 = Class              #21            // Test0315/TestFor
   #5 = Class              #22            // java/lang/Object
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               main
  #11 = Utf8               ([Ljava/lang/String;)V
  #12 = Utf8               StackMapTable
  #13 = Class              #23            // "[I"
  #14 = Utf8               SourceFile
  #15 = Utf8               TestFor.java
  #16 = NameAndType        #6:#7          // "<init>":()V
  #17 = Class              #24            // java/lang/System
  #18 = NameAndType        #25:#26        // out:Ljava/io/PrintStream;
  #19 = Class              #27            // java/io/PrintStream
  #20 = NameAndType        #28:#29        // println:(I)V
  #21 = Utf8               Test0315/TestFor
  #22 = Utf8               java/lang/Object
  #23 = Utf8               [I
  #24 = Utf8               java/lang/System
  #25 = Utf8               out
  #26 = Utf8               Ljava/io/PrintStream;
  #27 = Utf8               java/io/PrintStream
  #28 = Utf8               println
  #29 = Utf8               (I)V
{
  public Test0315.TestFor();
    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

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=3, args_size=1
         0: iconst_5
         1: newarray       int
         3: dup
         4: iconst_0
         5: bipush        6
         7: iastore
         8: dup
         9: iconst_1
        10: bipush        7
        12: iastore
        13: dup
        14: iconst_2
        15: bipush        8
        17: iastore
        18: dup
        19: iconst_3
        20: bipush        9
        22: iastore
        23: dup
        24: iconst_4
        25: bipush        10
        27: iastore
        28: astore_1
        29: iconst_0
        30: istore_2
        31: iload_2
        32: aload_1
        33: arraylength
        34: if_icmpge     52
        37: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        40: aload_1
        41: iload_2
        42: iaload
        43: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        46: iinc          2, 1
        49: goto          31
        52: return
      LineNumberTable:
        line 13: 0
        line 14: 29
        line 15: 37
        line 14: 46
        line 17: 52
      StackMapTable: number_of_entries = 2
        frame_type = 253 /* append */
          offset_delta = 31
          locals = [ class "[I", int ]
        frame_type = 250 /* chop */
          offset_delta = 20
}
SourceFile: "TestFor.java"

六、结果分析

  • 我们从反编译出来的结果中可以很清楚的看到,Collection集合(ArrayList)在使用 foreach 时,是通过迭代器 Iterator,不断获取 next 元素,而数组没有这样做,在我们观察数组在使用普通 for 与 foreach 两个反编译结果时发现,数组的 foreach 结果与 for 几乎可以说是一模一样,可以看到网上一些说法描述道:“数组也可以使用 foreach 循环遍历,是因为Java将对于数组的 foreach 循环转换为对于这个数组每一个的循环引用”,我想这句话更直观或者更准确的表达应该是:数组使用 foreach 时,是把 foreach 转换为普通 for 来循环处理的,与 Iterable 无关
  • 所以我们需要记住:除了实现 Iterable 接口可以使用 foreach 外,数组也是可以使用 foreach 的,在数组使用 foreach 时,是把 foreach 转换为普通 for 来循环处理
上一篇:JVM性能调优(1) —— JVM内存模型和类加载运行机制


下一篇:Lambda 表达式介绍和底层实现分析