在探索Java的增强型for循环时,我编写了以下测试:
class test
{
int number = 0;
public static void main(String args[]) {
new test();
}
public test() {
int[] numbers = getNumbers();
for(int number : numbers) {
System.out.println("number : " + number);
System.out.println("numbers[0]: " + numbers[0]);
numbers = getNumbers();
}
}
public int[] getNumbers() {
number++;
int[] numbers = new int[5];
for(int i = 0; i < numbers.length; i++)
numbers[i] = number;
return numbers;
}
}
我很惊讶地发现我的测试结果出来了:
number : 1
numbers[0]: 1
number : 1
numbers[0]: 2
number : 1
numbers[0]: 3
number : 1
numbers[0]: 4
number : 1
numbers[0]: 5
然后看起来在首次执行循环时创建了一个单独的numbers
实例,并使其成为不可变的。从那里,对numbers
所做的任何更改都只对存在于循环条件之外的版本进行。
我已经通过用以下代码替换循环来确认行为至少与此类似:
for(int number : numbers) {
System.out.println("number : " + number);
numbers = null;
}
给出了:
number : 1
number : 1
number : 1
number : 1
number : 1
我的问题是:我们在这里隐藏了什么行为?是否复制了numbers
的第二个版本?它实际上是不可改变的,还是我没有用力戳它?换句话说:
在增强型for循环的引擎下发生了什么?
答案 0 :(得分:4)
for
循环
Iterable
个对象:增强的
for
语句相当于表单的基本for
语句:for (I #i = Expression.iterator(); #i.hasNext(); ) { {VariableModifier} TargetType Identifier = (TargetType) #i.next(); Statement }
增强的
for
语句相当于表单的基本for
语句:T[] #a = Expression; L1: L2: ... Lm: for (int #i = 0; #i < #a.length; #i++) { {VariableModifier} TargetType Identifier = #a[#i]; Statement }
它创建一个对数组的新引用,与原始引用分开。您可以根据需要修改对数组的引用,但它仍将继续迭代对原始数组的隐式引用。
超过Iterable
s,只保留对Iterator
的引用,即使您在循环期间重新分配原始变量,它仍会返回原始Iterable
对象。 / p>
无论是对数组还是for
使用增强型Iterable
循环,都可以重新分配用作目标的引用,而不会影响迭代结果 - 您仍将迭代原始数据目标参考引用的对象。
答案 1 :(得分:1)
在增强的for循环中,如下所示:
for(int number : numbers) {
:
之后的任何表达式只被评估一次:当for循环开始时。即使您在循环运行时为numbers
变量设置了不同的值,Java也已缓存numbers
的原始值并迭代该值。如果在循环后打印numbers
中的值,您会看到numbers
确实发生了变化,但for循环迭代的实际数组没有。
...
public test() {
int[] numbers = getNumbers();
System.out.println("Array before loop starts:");
System.out.println(Arrays.toString(numbers) + "\n");
for (int number : numbers) {
System.out.println("number : " + number);
System.out.println("numbers[0]: " + numbers[0]);
numbers = getNumbers();
}
System.out.println("\nArray after loop finishes:");
System.out.println(Arrays.toString(numbers));
}
...
以上版画:
Array before loop starts:
[1, 1, 1, 1, 1]
number : 1
numbers[0]: 1
number : 1
numbers[0]: 2
number : 1
numbers[0]: 3
number : 1
numbers[0]: 4
number : 1
numbers[0]: 5
Array after loop finishes:
[6, 6, 6, 6, 6]
答案 2 :(得分:0)
引用JLS:
增强的
for
语句相当于表单的基本for
语句:T[] #a = Expression; L1: L2: ... Lm: for (int #i = 0; #i < #a.length; #i++) { {VariableModifier} TargetType Identifier = #a[#i]; Statement }
观察Expression
仅评估一次。
答案 3 :(得分:0)
来自java docs
“例如,此代码:
List<? extends Integer> l = ...
for (float i : l) ...
将被翻译为:
for (Iterator<Integer> #i = l.iterator(); #i.hasNext(); ) {
float #i0 = (Integer)#i.next();
...
https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.14.2
所以是的,你的观察是正确的!
答案 4 :(得分:-1)
然后会出现一个单独的数字实例......
嗯......是的。您通过运行new int[5]
6次创建了五个新实例。并且for循环继续使用第一个。第一个没有收集垃圾,直到for循环结束,即使你不能再引用它了。
不建议在循环播放时更改集合。