为什么下面的工作正常?

String str;
while (condition) {
    str = calculateStr();
    .....
}

但是下面这个被认为是危险的/不正确的:

while (condition) {
    String str = calculateStr();
    .....
}

有必要在循环之外声明变量吗?


当前回答

这两个例子的结果是一样的。但是,第一个函数为你提供了在while循环之外使用str变量的方法;第二个则不然。

其他回答

我比较了这两个(相似的)例子的字节代码:

我们来看1。例子:

package inside;

public class Test {
    public static void main(String[] args) {
        while(true){
            String str = String.valueOf(System.currentTimeMillis());
            System.out.println(str);
        }
    }
}

在javac Test.java, javap -c Test之后,你会得到:

public class inside.Test extends java.lang.Object{
public inside.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #2; //Method java/lang/System.currentTimeMillis:()J
   3:   invokestatic    #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
   6:   astore_1
   7:   getstatic       #4; //Field java/lang/System.out:Ljava/io/PrintStream;
   10:  aload_1
   11:  invokevirtual   #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   14:  goto    0

}

让我们看看2。例子:

package outside;

public class Test {
    public static void main(String[] args) {
        String str;
        while(true){
            str =  String.valueOf(System.currentTimeMillis());
            System.out.println(str);
        }
    }
}

在javac Test.java, javap -c Test之后,你会得到:

public class outside.Test extends java.lang.Object{
public outside.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #2; //Method java/lang/System.currentTimeMillis:()J
   3:   invokestatic    #3; //Method java/lang/String.valueOf:(J)Ljava/lang/String;
   6:   astore_1
   7:   getstatic       #4; //Field java/lang/System.out:Ljava/io/PrintStream;
   10:  aload_1
   11:  invokevirtual   #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   14:  goto    0

}

观察结果表明,这两个例子没有什么不同。这是JVM规范的结果……

但是为了最佳编码实践的名义,建议在尽可能小的范围内声明变量(在本例中,它是在循环内部,因为这是唯一使用变量的地方)。

在while循环之外声明String str允许它在while循环内外被引用。在while循环中声明String str只允许它在while循环中被引用。

正如很多人指出的那样,

String str;
while(condition){
    str = calculateStr();
    .....
}

没有比这更好的了:

while(condition){
    String str = calculateStr();
    .....
}

因此,如果你不重用变量,就不要在变量作用域之外声明变量。

我认为物体的大小也很重要。 在我的一个项目中,我们声明并初始化了一个大型二维数组,该数组使应用程序抛出内存不足异常。 我们将声明移出循环,并在每次迭代开始时清除数组。

如果你不需要在while循环之后使用str(范围相关),那么第二个条件,即。

  while(condition){
        String str = calculateStr();
        .....
    }

如果你只在条件为真时才在堆栈上定义一个对象,那就更好了。也就是说,如果你需要,就使用它