前两个例子:
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);
...
}
在第一个示例中,引用变量myClass仅创建一次。但是在第二个例子中,它是仅创建一次,还是每次迭代创建一次?我的想法也许是编译器优化了这个,我不知道。
我试图通过编写一个例子来回答这个问题,但无法弄明白。如何通过代码证明?
注意:我意识到示例2是更好的样式,因为myClass在for循环之外是不可知的,并且它的范围保持最小。我也在这里搜索过但是没有找到这个确切问题的明确答案(通常它是&#34;这是首选的问题?&#34;。)我还假设如果myClass参考每次迭代都会创建它并不是一个很大的性能问题。
编辑:再次,我不是问哪种编码风格更好。此外,我想知道它是否可以通过代码推断/证明。我尝试生成并比较字节码,但我不熟悉字节码,生成的内容不完全匹配。
答案 0 :(得分: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);
}
}
反编译为:
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
两者完全相同。使用最具可读性的那个。
(*)我已将循环变量i
声明为参数,以避免在Object
之前或之后声明的i
导致的生成字节码的任何差异。这与字节码的唯一区别在于aload_N
/ iload_N
/ astore_N
/ istore_N
指令 - N
不同,因为变量存储在不同的“老虎机”。这不是一个显着的差异。