在循环内部或外部声明变量

2022-08-31 05:23:45

为什么以下工作正常?

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

但是这个据说是危险的/不正确的:

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

是否有必要在循环外部声明变量?


答案 1

局部变量的作用域应始终尽可能小。

在你的示例中,我假设没有在循环外部使用,否则您就不会提出问题,因为在循环内声明它不是一个选项,因为它不会编译。strwhilewhile

因此,由于 不在循环外部使用,因此 最小的可能范围是在 while 循环strstr

因此,答案是绝对应该在 while 循环中声明。没有如果,没有和,没有但是。str

可能违反此规则的唯一情况是,如果由于某种原因,必须从代码中挤出每个时钟周期至关重要,在这种情况下,您可能需要考虑在外部作用域中实例化某些内容并重用它,而不是在内部作用域的每次迭代中重新实例化它。但是,由于java中字符串的不可变性,这不适用于您的示例:str的新实例将始终在循环的开头创建,并且必须在循环结束时将其丢弃,因此无法在那里进行优化。

编辑:(在答案下面注入我的评论)

无论如何,正确的做事方法是正确编写所有代码,为产品建立性能要求,根据此要求衡量最终产品,如果它不满足它,那么就去优化它。通常最终会发生的事情是,你找到方法在几个地方提供一些漂亮而正式的算法优化,使我们的程序满足其性能要求,而不必遍历整个代码库,调整和破解东西,以挤压这里的时钟周期。


答案 2

我比较了这两个(类似)示例的字节码:

让我们看一下 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.javajavap -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.javajavap -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 规范的结果...

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


推荐