是否为每次迭代创建了for循环中的引用变量声明?

itD*_*ing 1 java java-memory-model

前两个例子:

1)
MyClass myClass;
for (int i=0; i<arrayList.size(); i++) {
    myClass = arrayList.get(i);
    ...
}


2)
for (int i=0; i<arrayList.size(); i++) {
    MyClass myClass = arrayList.get(i);
    ...
}
Run Code Online (Sandbox Code Playgroud)

在第一个示例中,引用变量myClass仅创建一次.但是在第二个例子中,它是仅创建一次,还是每次迭代创建一次?我的想法也许是编译器优化了这个,我不知道.

我试图通过编写一个例子回答这个问题,但无法弄明白.如何通过代码证明?

注意:我意识到示例2是更好的样式,因为myClass在for循环之外是未知的,并且它的范围保持最小.我也在这里搜索过,但是没有找到这个确切问题的确定答案(通常这是一个"哪个是首选的?"的问题.)我还假设如果每次迭代都创建了myClass引用,那么它不是一个大的表现问题.

编辑:再说一遍,我不是问哪种编码风格更好.此外,我想知道它是否可以通过代码推断/证明.我试图生成并比较字节码,但我不熟悉字节码,生成的内容不完全匹配.

And*_*ner 6

声明变量时,不要"创建"任何内容.变量只是一个方便的名称,可以帮助您记住放置结果的位置,并允许您控制结果的使用位置,并将某些类型信息与该结果相关联; 但是一旦编译完代码,它们就不再存在了.

比较字节码(*):

void outside(int i, List<?> list) {
  Object obj;
  for (i = 0; i < list.size(); i++) {
    obj = list.get(i);
  }
}

void inside(int i, List<?> list) {
  for (i = 0; i < list.size(); i++) {
    Object obj = list.get(i);
  }
}
Run Code Online (Sandbox Code Playgroud)

反编译为:

  void outside(int, java.util.List<?>);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: aload_2
       4: invokeinterface #2,  1            // InterfaceMethod java/util/List.size:()I
       9: if_icmpge     26
      12: aload_2
      13: iload_1
      14: invokeinterface #3,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      19: astore_3
      20: iinc          1, 1
      23: goto          2
      26: return

  void inside(int, java.util.List<?>);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: aload_2
       4: invokeinterface #2,  1            // InterfaceMethod java/util/List.size:()I
       9: if_icmpge     26
      12: aload_2
      13: iload_1
      14: invokeinterface #3,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      19: astore_3
      20: iinc          1, 1
      23: goto          2
      26: return
Run Code Online (Sandbox Code Playgroud)

两者是相同的.使用最易读的.


(*)我已经将循环变量声明i为参数,以避免由于在Object之前或之后声明而导致的生成字节码的任何差异i.这对字节码的唯一区别在于aload_N/ iload_N/ astore_N/ istore_N指令 - N因为变量存储在不同的"槽"中,所以它们是不同的.这不是一个显着的差异.