在循环中创建对象
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);
这将为循环的每次迭代创建一个新的Object
o。
即使对于可变对象,无论哪种方式,您都会得到相同的结果!
【讨论】:
两者都创建相同数量的字符串,因为两者都没有创建任何字符串。 @HotLicks 你到底在胡说什么? 在原始代码(自编辑后)中,任何地方都没有创建任何字符串。 “foo”是预先存在的字符串文字。 @HotLicks 我的原始答案中的代码为零,所以我认为您只是放错了评论。 我说的是原始问题。它已被编辑为与第一个版本完全不同。【参考方案9】:不同之处在于,在第二种情况下,当循环结束时,您的 a
变量仍将在范围内
除此之外,它们本质上是相同的,即使从垃圾收集的角度来看也是如此。
字符串是引用类型(尽管是不可变的),无论您是为它们声明一个新变量还是每次都覆盖同一个变量,这并不重要。您仍然每次都在创建一个全新的字符串。
【讨论】:
请澄清“每次都创建一个全新的字符串”。我不记得创建任何字符串的任何版本的 OP 代码。 实际上,不,OP没有编辑它,其他人做了。如果您检查编辑历史记录,则从来没有一个版本执行过new String
操作。
@SamIam 每次迭代中的(变量的)声明是否有任何开销?还是还是一样。
@OptimusPrime 如果有开销,那么您还不足以注意到差异。以上是关于在循环中创建对象的主要内容,如果未能解决你的问题,请参考以下文章