12

我有一个简单的问题。通常我写这样的代码:

String myString = "hello";

for (int i=0, i<10; i++)
{
  myString = "hello again";
}

因为我认为以下样式不是很好的样式,因为它会创建太多不必要的对象。

for (int i=0, i<10; i++)
{
  String myString = "hello again";
}

这甚至正确吗?或者这只是当我从我创建的类中获得一个明确的对象(如对象)时的情况?如果它是一个布尔值或整数呢?什么是更好的编码风格?在循环之前实例化一次并在循环中使用它还是每次在循环中再次实例化它?为什么?因为程序更快或使用更少的存储空间或......?

有人告诉我,如果它是一个布尔值,我应该直接在循环中实例化它。他说这不会对堆产生影响,而且变量属于循环内部会更清楚。那么什么是正确的呢?

感谢您的回答!:-)

====

感谢您的所有回答!

总之:最好在尽可能小的范围内声明一个对象。通过在循环外声明和实例化对象并没有提高性能,即使在每个循环中都重新实例化了对象。

4

7 回答 7

15

不,后面的代码实际上是无效的。不过,它会带有大括号:

for (int i=0; i<10; i++)
{
    String myString = "hello again";
}

(基本上,您不能将变量声明用作if语句、循环等的单语句体。)

这将毫无意义,但有效 - 并且比第一个版本 IMO更可取。它不需要更多的内存,但通常最好为局部变量提供尽可能窄的范围,尽可能晚地声明,理想情况下在同一点初始化。它使每个变量的使用位置更加清晰。

当然,如果您需要在循环外(之前或之后)引用变量,那么您也需要在循环外声明它。

考虑效率时,需要区分变量对象。上面的代码最多使用一个对象——字面“hello again”所指的 String 对象。

于 2012-05-09T19:38:27.930 回答
3

正如 Binyamin Sharet 所提到的,您通常希望在尽可能小的范围内声明一个变量。在您的具体示例中,第二个通常更可取,除非您需要访问循环外的变量。

但是,在某些情况下,这可能会对性能产生影响——即,如果您一遍又一遍地实例化同一个对象。在您的特定示例中,您受益于 Java 的字符串文字自动池。但是假设您实际上在循环的每次迭代中都创建了同一对象的新实例,并且该循环被执行了数百或数千次:

for (int i=0, i<1000; i++)
{
  String myString = new String("hello again"); // 1000 Strings are created--one on every iteration
  ...
}

如果您的循环循环了数百或数千次,但碰巧您一遍又一遍地实例化同一个对象,那么在循环内实例化它会导致大量不必要的垃圾收集,因为您创建并抛出在每次迭代中删除一个新对象。在这种情况下,最好在循环之外声明和实例化变量:

String myString = new String("hello again"); // only one String is created

for (int i=0, i<1000; i++)
{
  ...
}

而且,为了完整的循环,您可以通过在相关代码部分周围添加额外的大括号来手动限制范围:

{ // Limit the scope
  String myString = new String("hello again");

  for (int i=0, i<1000; i++)
  {
    ...
  }
}
于 2012-05-09T20:04:18.200 回答
1

第二个问题是您创建对象并且有人(GC)必须清理它们,当然对于 10 次迭代来说这并不重要。

顺便说一句,在你的具体例子中,我会写

    String myString = null; 
    final String HELLO_AGAIN="hello again";
    for (int i=0; i<10; i++)
      myString = HELLO_AGAIN;
于 2012-05-09T19:51:55.973 回答
1

似乎您的意思是declare,不是instantiate,一般来说,您应该在所需的最小范围内声明一个变量(在这种情况下 - 在循环中)。

于 2012-05-09T19:37:58.603 回答
1

如果要在 for 循环之外使用变量,则将其声明在外面,否则最好将范围保持在最小

于 2012-05-09T19:39:39.643 回答
0

除非值被改变,否则你绝对应该在循环之外实例化。

于 2012-05-09T19:38:16.107 回答
0

这里的问题是 String 是一个不可变对象:您不能更改字符串的值,只能创建新的 String 对象。无论哪种方式,如果您的目标是为变量分配一个新的对象实例,那么限制您的范围并在循环体中声明它。

如果您的对象是可变的,那么在循环的每次下一次迭代中重用该对象是合理的,并且只需更改您需要的那些属性。此概念用于多次运行相同的查询,但使用不同的参数,您使用 PreparedStatement。

在极端情况下,您甚至可以维护可以在整个应用程序中共享的对象池。当您耗尽资源时,您会创建额外的对象,如果您检测到合理数量的未使用,您会缩小。这个概念用于维护一个连接池。

于 2013-04-07T18:06:31.207 回答