我在探索 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;
}
}
Run Code Online (Sandbox Code Playgroud)
我很惊讶地发现我的测试输出了:
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
Run Code Online (Sandbox Code Playgroud)
然后似乎numbers在第一次执行循环时创建了一个单独的实例,并且是不可变的。从那里开始,所做的任何更改numbers都只对存在于循环条件之外的版本进行。
我已经通过将循环替换为以下内容来确认行为至少与此类似:
for(int number : numbers) {
System.out.println("number : " + number);
numbers = null;
}
Run Code Online (Sandbox Code Playgroud)
这给了:
number : 1
number : 1
number : 1
number : 1
number : 1
Run Code Online (Sandbox Code Playgroud)
我的问题是:这里对我们隐藏了什么行为?是numbers复制的第二个版本吗?它实际上是不可变的,还是我不够用力地戳它?换句话说:
增强的 for 循环背后发生了什么?
JLS 第 14.14.2 节for涵盖了增强循环:
Iterable对象:增强
for语句等效于for以下形式的基本语句:Run Code Online (Sandbox Code Playgroud)for (I #i = Expression.iterator(); #i.hasNext(); ) { {VariableModifier} TargetType Identifier = (TargetType) #i.next(); Statement }
增强
for语句等效于for以下形式的基本语句:Run Code Online (Sandbox Code Playgroud)T[] #a = Expression; L1: L2: ... Lm: for (int #i = 0; #i < #a.length; #i++) { {VariableModifier} TargetType Identifier = #a[#i]; Statement }
它创建一个对数组的新引用,与您的原始引用分开。您可以根据需要修改对数组的引用,但它仍将继续迭代对原始数组的隐式引用。
在Iterables 上,只Iterator保留对 的引用Iterable,即使您在循环期间重新分配原始变量,它仍会引用回原始对象。
无论您for对数组还是使用增强循环Iterable,您都可以重新分配用作目标的引用,而不会影响迭代结果——您仍将迭代目标引用所引用的原始对象。