本文正在参加「Java主题月 – Java Debug笔记活动」,详情查看 活动链接
Debug 笔记
<为什么Java中的文件名与公共类名相同?>
提问
我很惊讶地看到下面的Java代码片段已编译并运行:
for(final int i : listOfNumbers) {
System.out.println(i);
}
复制代码
其中listOfNumbers
是整数数组。
我以为最终声明只分配了一次。
编译器是否正在创建Integer
对象并更改其引用?
回答一:
想象一下,速记看起来很像这样:
for (Iterator<Integer> iter = listOfNumbers.iterator(); iter.hasNext(); )
{
final int i = iter.next();
{
System.out.println(i);
}
}
复制代码
回答二:
有关涉及的范围规则的说明,请参见@TravisG
。之所以使用像这样的最终循环变量的唯一原因是,如果您需要在匿名内部类中对其进行关闭。
import java.util.Arrays;
import java.util.List;
public class FinalLoop {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(new Integer[] { 0,1,2,3,4 });
Runnable[] runs = new Runnable[list.size()];
for (final int i : list) {
runs[i] = new Runnable() {
public void run() {
System.out.printf("Printing number %d\n", i);
}
};
}
for (Runnable run : runs) {
run.run();
}
}
}
复制代码
循环变量在执行时不属于Runnable
的范围,仅在实例化时才属于。如果没有final
关键字,则当Runnable
最终运行时,循环变量将对Runnable
不可见。即使是这样,所有Runnable
的值都相同。
顺便说一句,大约10年前,在局部变量上使用final可能会看到很小的速度改进(在极少数情况下);很长一段时间都不是这种情况了。现在,使用final
的唯一原因是允许您使用这样的词法闭包。
再回答@mafutrct:
编写代码时,需要两个受众。第一个是计算机,只要它在语法上是正确的,它将对您做出的任何选择感到满意。第二个是为将来的读者(通常是您自己)准备的,这里的目标是传达代码的“意图”,而不会模糊代码的“功能”。语言特征应惯用,尽量少用解释,以减少歧义。
在使用循环变量的情况下,可以使用final
来传达以下两种情况之一:单分配
;或者,关闭
。对循环进行一次简单的扫描将告诉您是否重新分配了循环变量。但是,由于创建闭包时两个执行路径的交织,很容易错过闭包意图捕获变量的情况。当然,除非您仅使用final
表示要捕获的意图,否则对于读者来说,这是正在发生的事情。
文章翻译自Stack Overflow
:stackoverflow.com/questions/3…