一尘不染

Java-在循环内或循环外声明变量

java

为什么以下工作正常?

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

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

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

是否需要在循环外声明变量?


阅读 522

收藏
2020-02-27

共2个答案

一尘不染

局部变量的范围应始终尽可能小。
在你的例子我相信str是不会使用的外while循环,否则你就不会问这个问题,因为它声明的内部while循环不会是一个选项,因为它不会编译。

所以,既然str是不使用外循环,在尽可能小的范围str是内 while循环。

所以,答案是着重那str绝对应该被while循环内声明。没有,没有,没有,但是。

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

编辑:(将我的评论插入下面的答案中)

在任何情况下,正确的处理方式是正确编写所有代码,为产品建立性能要求,根据该要求评估最终产品,如果不满足要求,则进行优化。通常最终会发生的事情是,你找到了在几个地方提供一些不错的正式算法优化的方法,从而使我们的程序能够满足其性能要求,而不必遍历整个代码库并对其进行调整和修改。为了在这里和那里挤压时钟周期。

2020-02-27
一尘不染

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

让我们看一下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. example:

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规范的结果…

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

2020-02-27