我对变量范围有一个相当简单的问题。
我熟悉增强的For-Loops,但我不明白为什么我应该声明一个新变量来保存每个元素。一个例子可能会澄清我的问题:
int[] ar = {1, 2, 3};
int i = 0;
for(i : ar) { // this causes an error if I do not declare a new variable: int i
// for(int i : ar) // this works fine
System.out.println(i);
}
那么我为什么要声明这个新变量呢?在for循环中可以访问所有i
之后。我不想使用任何先前的i
值,只是不想声明一个新变量。 (我猜测其他可迭代的项目使用相同的变量可能会更快)。
我想这就是增强For For-Loop的构建方式,但这不会破坏整个范围的想法吗?
上述行为引发了一个问题。编译器是否对整个for
循环使用相同的变量并只更新其值,或者为每次迭代创建新变量?
一个有趣的部分是,如果我同时保留 int i 的声明(在for
循环之前和之内),我甚至会遇到编译器错误
重复的局部变量i
这使得(至少对我来说)事情有点奇怪。所以我不能在i
循环中使用先前声明的变量for
,但我也不能在其中声明一个具有相同名称的新变量。
答案 0 :(得分:4)
那么我为什么要声明这个新变量?
因为这是定义语法的方式。
毕竟我可以在for循环中访问。
这个语义。它与语法无关。
我不想使用i的任何先前值,只是不想声明一个新变量。 (我猜测其他可迭代的项目使用相同的变量可能会更快)。
不要猜测性能。测试和测量。但在这种情况下,无法衡量,因为任何工作代码都比任何非工作代码都快。
答案 1 :(得分:2)
这是否意味着我有一个局部变量在每个循环中获得不同的值或不同的变量?
从语言的角度来看,每次迭代都有一个不同的变量。这就是你可以写的原因:
for(final ItemType item: iterable) {
…
}
这会产生很大的不同,因为您可以在循环内创建引用当前元素的内部类实例。使用Java 8,您也可以使用lambdas,甚至省略final
修饰符,但语义不会改变:您不会像在C#中那样得到令人惊讶的结果。
我猜测其他可迭代项目使用相同的变量
可能会更快
那是胡说八道。只要您不知道生成的代码是如何形成的,您甚至不应该猜测。
但是如果您对Java字节代码的细节感兴趣:在堆栈框架内,局部变量由数字而不是名称来寻址。并且通过重用超出范围的局部变量的存储,将程序的局部变量映射到这些存储位置。变量在整个循环期间是存在还是在每次迭代时“重新创建”都没有区别。它仍然只占用堆栈帧中的一个插槽。因此,尝试在源代码级别上“重用局部变量”根本就没有意义。它只会降低你的程序的可读性。
答案 2 :(得分:1)
这里只是引用:JLS Section 14.14.2, The enhanced for statement定义增强的for循环以具有以下结构(与此问题相关):
EnhancedForStatement: for ( {VariableModifier} UnannType VariableDeclaratorId : Expression ) Statement
其中UnannType可归纳为"类型" (原始,参考......)。因此,根据语言规范,给出循环变量的类型只是强制性的 - 导致问题中描述的(不可否认的:有些混乱)观察。
答案 3 :(得分:0)
程序中的int i
对于for循环是可见的,并且在同一范围内可能是其下面的循环(如果存在)。但是for(int i : ar)
中的i是for循环的本地。因此,一旦循环的执行结束就结束。这是为foreach循环定义的语法,“你必须使用范围限于循环的变量”。
So why I should declare this new variable? After all i is accessible inside the for loop. I did not want to use any previous value of i, just did not want to declare a new variable. (I guessed for other iterable items it might be faster using the same variable).
如果你反复使用相同的变量微小原始变量而不是仅在需要时创建一个变量并且在循环结束后被破坏,那么为什么会有相当大的性能优势呢。
答案 4 :(得分:0)
除了宣称那是语法之外,我认为没有人回答原始问题。我们都知道这就是语法。从逻辑上讲,问题是,为什么? 毕竟,只要循环是非增强的for循环,就可以使用在循环之前定义的变量作为循环变量!