增强的for循环中局部变量的范围
关于变量范围,我有一个相当简单的问题。
我熟悉增强的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); }
那么我为什么要声明这个新变量呢? 毕竟i
可以在for循环中访问。 我不想使用任何以前的i
值,只是不想声明一个新变量。 (我猜测其他可迭代项目使用相同的变量可能会更快)。
我想这就是增强For For-Loop的构建方式,但这不会破坏整个范围的想法吗?
上述行为引发了一个问题。 编译器是否对整个for
循环使用相同的变量并只更新其值,或者为每次迭代创建一个新变量 ?
一个有趣的部分是,如果我保留int i的声明(在for
循环之前和之内),我甚至会得到编译器错误
重复的局部变量i
这使得(至少对我来说)事情有点奇怪。 所以我不能在for
循环中使用先前声明的变量i
for
但我也不能在其中声明一个具有相同名称的新变量。
那么我为什么要声明这个新变量呢?
因为这是定义语法的方式。
毕竟我可以在for循环中访问。
这是语义。 这与语法无关。
我不想使用任何以前的i值,只是不想声明一个新变量。 (我猜测其他可迭代项目使用相同的变量可能会更快)。
不要猜测性能。 测试和测量。 但在这种情况下,没有什么可衡量的,因为任何工作代码都比任何非工作代码更快。
这是否意味着我有一个局部变量在每个循环中获得不同的值或不同的变量?
从语言的角度来看,每次迭代都有一个不同的变量。 这就是为什么你可以写:
for(final ItemType item: iterable) { … }
这会产生很大的不同,因为你可以在循环中创建引用当前元素的内部类实例。 使用Java 8,您也可以使用lambdas,甚至可以省略final
修饰符,但语义不会改变:您不会像在C#中那样得到令人惊讶的结果。
我猜测其他可迭代项目使用相同的变量可能会更快
那是胡说八道。 只要您不知道生成的代码是如何形成的,您甚至不应该猜测。
但是如果您对Java字节代码的细节感兴趣:在堆栈框架内,局部变量由数字而不是名称来寻址。 并且通过重用超出范围的局部变量的存储,将程序的局部变量映射到这些存储位置。 变量是存在于整个循环期间还是在每次迭代时“重新创建”都没有区别。 它仍然只占用堆栈帧中的一个插槽。 因此,尝试在源代码级别上“重用局部变量”根本就没有意义。 它只是使你的程序可读性降低。
只是在这里引用: JLS Section 14.14.2,增强的for语句定义了增强的for循环,它具有以下结构(与此问题相关):
EnhancedForStatement: for ( {VariableModifier} UnannType VariableDeclaratorId : Expression ) Statement
其中UnannType可以概括为“类型”(原始,引用……)。 因此,根据语言规范给出循环变量的类型是必须的 – 导致问题中描述的(不可否认的:有点混乱)观察。
程序中的int i
对于for循环是可见的,并且可能在同一范围内的其他循环(如果存在)下可见。 但是for(int i : ar)
是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).
如果你反复使用相同的变量微小原始变量而不是仅在需要时创建一个变量并且在循环结束后被破坏,那么为什么会有相当大的性能优势呢?
我认为除了宣称那是语法之外,没有人回答原始问题。 我们都知道这就是语法。 从逻辑上讲,问题是,为什么? 毕竟,只要循环是非增强的for循环,就可以使用在循环之前定义的变量作为循环变量!