为什么下面的工作正常?

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

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

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

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


当前回答

局部变量的作用域应该总是尽可能的小。

在你的例子中,我假设str没有在while循环之外使用,否则你就不会问这个问题,因为在while循环内部声明它不是一个选项,因为它不会编译。

因此,由于str不在循环之外使用,因此str的最小作用域是在while循环内。

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

The only case where this rule might be violated is if for some reason it is of vital importance that every clock cycle must be squeezed out of the code, in which case you might want to consider instantiating something in an outer scope and reusing it instead of re-instantiating it on every iteration of an inner scope. However, this does not apply to your example, due to the immutability of strings in java: a new instance of str will always be created in the beginning of your loop and it will have to be thrown away at the end of it, so there is no possibility to optimize there.

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

In any case, the right way to do things is to write all your code properly, establish a performance requirement for your product, measure your final product against this requirement, and if it does not satisfy it, then go optimize things. And what usually ends up happening is that you find ways to provide some nice and formal algorithmic optimizations in just a couple of places which make our program meet its performance requirements instead of having to go all over your entire code base and tweak and hack things in order to squeeze clock cycles here and there.

其他回答

如果你的calculateStr()方法返回null,然后你试图在str上调用一个方法,你有NullPointerException的风险。

更一般地说,避免使用空值变量。顺便说一下,它对类属性更强。

局部变量的作用域应该总是尽可能的小。

在你的例子中,我假设str没有在while循环之外使用,否则你就不会问这个问题,因为在while循环内部声明它不是一个选项,因为它不会编译。

因此,由于str不在循环之外使用,因此str的最小作用域是在while循环内。

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

The only case where this rule might be violated is if for some reason it is of vital importance that every clock cycle must be squeezed out of the code, in which case you might want to consider instantiating something in an outer scope and reusing it instead of re-instantiating it on every iteration of an inner scope. However, this does not apply to your example, due to the immutability of strings in java: a new instance of str will always be created in the beginning of your loop and it will have to be thrown away at the end of it, so there is no possibility to optimize there.

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

In any case, the right way to do things is to write all your code properly, establish a performance requirement for your product, measure your final product against this requirement, and if it does not satisfy it, then go optimize things. And what usually ends up happening is that you find ways to provide some nice and formal algorithmic optimizations in just a couple of places which make our program meet its performance requirements instead of having to go all over your entire code base and tweak and hack things in order to squeeze clock cycles here and there.

这个问题的一个解决方案是提供一个变量作用域来封装while循环:

{
  // all tmp loop variables here ....
  // ....
  String str;
  while(condition){
      str = calculateStr();
      .....
  }
}

当外部作用域结束时,它们将自动取消引用。

str变量将可用,并在内存中保留一些空间,即使在执行下面的代码之后。

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

str变量将不可用,内存也将被释放,这是在下面的代码中为str变量分配的。

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

如果我们采用第二种方法,肯定会减少系统内存,提高性能。

在最小范围内声明对象可以提高可读性。

性能对于今天的编译器来说并不重要。(在此场景中) 从维护的角度来看,第二种选择更好。 在同一个地方声明和初始化变量,在尽可能窄的范围内。

正如Donald Ervin Knuth所说:

“我们应该忘记小的效率,大约97%的时候: 过早的优化是万恶之源”

例如,程序员让性能考虑影响一段代码的设计的情况。这可能导致设计不那么清晰,或者代码不正确,因为优化使代码变得复杂,而程序员被优化分散了注意力。