有限的范围是最好的
使用您的第二个选项:
for ( ... ) {
String s = ...;
}
范围不影响性能
如果您反汇编从每个编译的代码(使用 JDK 的javap
工具),您将看到循环在两种情况下都编译为完全相同的 JVM 指令。另请注意,Brian R. Bondy 的“选项 #3”与选项 #1 相同。使用更严格的范围时,不会从堆栈中添加或删除任何额外的内容,并且在两种情况下都在堆栈上使用相同的数据。
避免过早初始化
两种情况之间的唯一区别是,在第一个示例中,变量s
被不必要地初始化。这是与变量声明位置不同的问题。这增加了两条浪费的指令(加载字符串常量并将其存储在堆栈帧槽中)。一个好的静态分析工具会警告您,您永远不会读取您分配给 的值s
,并且一个好的 JIT 编译器可能会在运行时忽略它。
您可以简单地通过使用空声明(即String s;
)来解决此问题,但这被认为是不好的做法,并且具有下面讨论的另一个副作用。
通常,将虚假值null
分配给变量只是为了消除编译器错误,即在未初始化变量的情况下读取变量。这个错误可以被认为是变量范围太大的提示,并且它在需要接收有效值之前被声明。空声明迫使您考虑每个代码路径;不要通过分配虚假值来忽略这个有价值的警告。
保留堆栈插槽
如前所述,虽然 JVM 指令在这两种情况下是相同的,但存在一个微妙的副作用,这使得在 JVM 级别上最好尽可能使用最有限的范围。这在该方法的“局部变量表”中可见。考虑一下如果你有多个循环,变量声明在不必要的大范围内会发生什么:
void x(String[] strings, Integer[] integers) {
String s;
for (int i = 0; i < strings.length; ++i) {
s = strings[0];
...
}
Integer n;
for (int i = 0; i < integers.length; ++i) {
n = integers[i];
...
}
}
变量s
和n
可以在它们各自的循环中声明,但由于它们不是,编译器在堆栈帧中使用两个“槽”。如果它们在循环中声明,编译器可以重用相同的槽,使堆栈帧更小。
真正重要的是什么
然而,这些问题大多无关紧要。一个好的 JIT 编译器会发现无法读取您浪费分配的初始值,并优化分配。在这里或那里保存一个插槽不会成就或破坏您的应用程序。
重要的是使您的代码可读且易于维护,在这方面,使用有限范围显然更好。变量的范围越小,就越容易理解它是如何使用的,以及对代码的任何更改都会产生什么影响。