在循环中创建对象

Posted

技术标签:

【中文标题】在循环中创建对象【英文标题】:Creating an Object inside a loop 【发布时间】:2014-03-18 14:35:30 【问题描述】:

是一种好习惯吗?我指向以下代码:

for(some condition)
    SomeClass a = new SomeClass ();
    System.out.println(a);

所以这将为每次迭代创建一个新的 SomeClass 实例。所以实例数将等于迭代次数。然后这些将被 GC 收集。

在循环中重用 SomeClass 对象会更好吗?像这样的:

SomeClass a = null;

for(some condition) 
    a = new SomeClass();
    System.out.println(a);

据我了解,第二种方法更好,因为它只会创建一次 SomeClass 对象,并在每次迭代中重用它。但我很怀疑。请确认这一点,或者让我知道我的基础不正确的地方。

【问题讨论】:

字符串字面量在这里不是最好的例子,因为字符串池。此外,您的两个示例都会在每次循环迭代中创建“新实例”。 好的,现在我取消了,那么集合和列表呢。如果我在循环内或循环外创建一个 Set 对象会有什么区别? 它们是对象,就像字符串一样。从这个角度来看,它们的行为完全相同。 这两个示例都没有创建对象。声明引用不等于创建对象,字符串字面量是预先创建好的。 @Pshemo - 是的,除非用户在循环之外对a 有一些用处,否则他选择哪个没有区别。即使生成的代码存在非常细微的差异,它对性能的影响也可以忽略不计(对 GC 没有影响)。 【参考方案1】:

注意不要混淆“对象”本身和“对象”的“引用”:

例如,以下代码创建了一个(空)引用,但没有创建对象。

Object a = null;

以下代码创建一个对象和对该对象的引用(该引用保存在名为“a”的变量中):

Object a = new Object();

以下代码创建新对象并“重新指向”现有(引用)变量以指向新对象:如果变量“a”已经拥有另一个引用,“a”会忘记它。 [但这并不意味着 other 变量可能仍然指向 'a' 引用的旧对象]。

a = new Object(); // it is the reference 'a' that is 're-used' here not the object...

每次在循环中重新运行上述语句时;您确实在创建一个新对象;并且您正在“重新指向”“a”到那个新对象。

每次都会忘记之前的引用(即'a'中保存的引用);并且(假设我们这里有一个单线程程序)这意味着它指向的对象现在将有零个引用指向它:这意味着该对象有资格进行垃圾收集。这个垃圾收集是否在这个时间点发生 - 我不知道我很害怕。

但我想说:就垃圾收集发生的时间而言,您的编码示例没有区别; 'pointer-type' 是否已经在循环外定义为 'Object',或者在循环内反复重新定义。

以下(无用)示例可能有助于说明代码一次性执行的“创建对象”和“指向引用”操作之间的区别:

// This creates an object ; but we don't hold a reference to it.
    public class TestClass 
    public static void main(String[] args) 
    for (int n=0;n<100;n++) 
        new Object();
    
    
    

对比:

// This creates a reference ; but no object is created
// This is identical to setting the reference to 'null'.
public class TestClass 
public static void main(String[] args) 
for (int n=0;n<100;n++) 
        Object o;



【讨论】:

更正:所有语句都没有创建新对象,因为引用字符串文字不会创建新对象。【参考方案2】:

唯一的区别是在第二种情况下,当循环结束时,变量仍然在范围内,不。在这两种情况下创建的对象的数量是相等的,因为字符串是不可变的

因为您刚刚编辑了问题,在这种情况下,在这两种情况下,每次迭代都会在内存中创建新对象

【讨论】:

【参考方案3】:

一切都与范围有关,

如果你用第二种方法:

SomeType someFunction()
   ...
    SomeClass a = null;

    for(some condition) 
        a = new SomeClass();


           System.out.println(a);
        
     ...
     return something
    

对象 a 将存在于内存中直到 someFunction 结束,而对于第一个方法,它的生命周期在循环的单次迭代中

【讨论】:

【参考方案4】:

据我所知 - 在更大的应用程序中(不在此),但在更大的应用程序中,最好使用 static block 来创建对象 - 因为静态块代码仅在类加载到内存时执行一次。从技术上讲,你可以在一个类中拥有多个静态块,尽管这没有多大意义

记住:Static block can access only static variables and methods

【讨论】:

不仔细考虑就使用静态的东西是非常不明智的。 @Hot Licks- 谢谢你的意见。 40 年编程经验支持的观点。【参考方案5】:

您将分配对象的变量与实际的对象实例混淆了。

两个代码示例都创建了等量的对象。第二个会将一个实例保留在更大的范围内,因此它的可用时间会更长。

【讨论】:

【参考方案6】:

第二个不是“更好”。

String a="foo"; 重用字符串池中的文字字符串。也就是说,无论您在loop 内/外声明a,在内存方面都没有区别。但是它们有不同的范围。我认为这是另一个问题。

即使用你编辑的版本,用一般SomeClass,也不是你想的那样:

第二种方法更好,因为这只会创建一次 SomeClass 对象,并且会在每次迭代中重用它。

它在每个循环步骤中创建新对象。 a 只是对对象的引用。关键是,如果您创建的对象被其他对象引用,GC 不会收集它,并释放内存。例如,旧的 (char[],因此 GC 不会清理原始字符串。

【讨论】:

关于池化的特定行为或字符串的要点......【参考方案7】:

由于话题发生了很大变化。我更新:

如果您真的想重用一次创建的对象,您将不得不自己编写该代码。它可以遵循这个原则:

SomeClass a = new SomeClass();

for(some condition) 
    a.reset();
    //do something with a

SomeClass.reset() 方法处理所有细节(取决于您对对象的实际使用情况)。

【讨论】:

a="foo" =\= new String("foo") a = "foo" 将“foo”的预先创建的 JVM 全局字符串实例分配给引用。所以什么都没有创建。 那有什么区别呢? “foo”和new String("foo")的区别?后者确实创建了一个新的 String 对象。 @SebastianH String a ="foo" 这里“foo”是一个字符串文字,存储在java字符串池中。如果您尝试将相同的文字分配给另一个引用,它将从字符串池中获得相同的引用。但是 new string(..) 返回一个字符串类型的对象【参考方案8】:

两者都创建了等量的字符串,因为String 是不可变的。每当为 String 分配一个新值时,就会创建一个新的 String

假设您打算在示例中使用可变对象。

选项 1

for(some condition)

    Object o = new Object();
    System.out.println(o);

这将为循环的每次迭代创建一个新的对象 o。

选项 2

Object o;
for(some condition)

    o = new Object();
    System.out.println(o);

这将为循环的每次迭代创建一个新的Objecto。

即使对于可变对象,无论哪种方式,您都会得到相同的结果!

【讨论】:

两者都创建相同数量的字符串,因为两者都没有创建任何字符串。 @HotLicks 你到底在胡说什么? 在原始代码(自编辑后)中,任何地方都没有创建任何字符串。 “foo”是预先存在的字符串文字。 @HotLicks 我的原始答案中的代码为零,所以我认为您只是放错了评论。 我说的是原始问题。它已被编辑为与第一个版本完全不同。【参考方案9】:

不同之处在于,在第二种情况下,当循环结束时,您的 a 变量仍将在范围内

除此之外,它们本质上是相同的,即使从垃圾收集的角度来看也是如此。

字符串是引用类型(尽管是不可变的),无论您是为它们声明一个新变量还是每次都覆盖同一个变量,这并不重要。您仍然每次都在创建一个全新的字符串。

【讨论】:

请澄清“每次都创建一个全新的字符串”。我不记得创建任何字符串的任何版本的 OP 代码。 实际上,不,OP没有编辑它,其他人做了。如果您检查编辑历史记录,则从来没有一个版本执行过new String 操作。 @SamIam 每次迭代中的(变量的)声明是否有任何开销?还是还是一样。 @OptimusPrime 如果有开销,那么您还不足以注意到差异。

以上是关于在循环中创建对象的主要内容,如果未能解决你的问题,请参考以下文章

在循环中动态创建对象

在循环中创建多个具有不同名称的对象以存储在数组列表中

如何在循环中创建动态对象?

在 Qt 中使用循环创建 QPixmap 对象?

javascript es6在循环中创建对象

坚持通过 for 循环创建对象