有限的范围是最好的
使用第二个选项:
for ( ... ) {
String s = ...;
}
范围不影响性能
如果您从每个代码中反汇编编译的代码(使用JDK的工具),您将看到循环在这两种情况下编译为完全相同的JVM指令。另请注意,Brian R. Bondy的“选项#3”与选项#1相同。使用更严格的范围时,不会在堆栈中添加或删除任何额外的内容,并且在这两种情况下,堆栈上都使用相同的数据。javap
避免过早初始化
这两种情况之间的唯一区别是,在第一个示例中,变量被不必要地初始化。这是一个与变量声明位置不同的问题。这增加了两个浪费的指令(加载字符串常量并将其存储在堆栈帧插槽中)。一个好的静态分析工具会警告你,你永远不会读取分配给 的值,一个好的JIT编译器可能会在运行时省略它。s
s
您可以简单地通过使用空声明(即 )来解决此问题,但这被认为是不好的做法,并且下面讨论另一个副作用。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编译器会发现,不可能读取你浪费分配的初始值,并优化分配。在这里或那里保存插槽不会成就或破坏您的应用程序。
重要的是使你的代码可读且易于维护,在这方面,使用有限的范围显然更好。变量的作用域越小,就越容易理解它的使用方式以及对代码的任何更改会产生什么影响。