Java:For-Each循环和引用

我想知道以下循环是否创建了对象的副本,而不是给我一个对它的引用。 原因是,因为第一个例子没有分配我的数组对象,但第二个例子没有。

MyObject objects[] = new MyObject[6]; for (MyObject o: objects) { o = new MyObject(); } MyObject objects[] = new MyObject[6]; for(int i = 0; i < objects.length; i++) { objects[i] = new MyObject(); } 

Java的工作方式与许多其他语言略有不同。 第一个例子中的o只是对象的引用。

当你说o = new MyObject() ,它会创建一个MyObject类型的新Object,并引用该对象的o ,而在o引用的objects[index]之前objects[index]

也就是说,objects [index]本身只是对内存中另一个对象的引用。 因此,为了将对象[index]设置为新的MyObject,您需要更改对象[index]指向的位置,这只能通过使用对象[index]来完成。

图片:(我糟糕的油漆技巧:D)

在此处输入图像描述

说明:这大致是Java内存管理的工作原理。 无论如何,不​​完全是粗略的。 你有对象,引用A1。 当您访问对象数组时,从开始参考点(A1)开始,然后向前移动X块。 例如,引用索引1会将您带到B1。 B1然后告诉你,你正在A2寻找对象。 A2告诉你它有一个位于C2的字段。 C2是整数,基本数据类型。 搜索完成。

o不引用A1或B1,而是引用C1或C2。 当你说new ... ,它会创建一个新对象并放在那里(例如,在插槽A3中)。 它不会影响A1或B1。

如果我能清楚一点,请告诉我。

简短的回答 :是的,有一个类似于副本的东西。

答案很长 :你发布的Java foreach循环是语法糖

 MyObject objects[] = new MyObject[6]; Iterator it = objects.iterator(); while (it.hasNext()) { MyObject o = it.next(); // The previous three lines were from the foreach loop // Your code inside the foreach loop o = new MyObject(); } 

正如desugared版本所示,设置一个等于foreach循环内部内容的引用不会改变数组的内容。

我在每个例子中添加了一条评论,以澄清发生了什么。

第一个例子:

 MyObject objects[] = new MyObject[6]; for(MyObject o: objects) { // Construct a new object of type MyObject and assign a reference to it into // the iteration variable o. This has no lasting effect, because the foreach // loop will automatically assign the next value into the iteration variable // in the the next iteration. o = new MyObject(); } 

第二个例子:

 MyObject objects[] = new MyObject[6]; for(int i = 0; i < objects.length; i++) { // Construct a new object of type MyObject and store a reference to it into the // i-th slot in array objects[]: objects[i] = new MyObject(); } 

第一个是不分配数组对象,因为foreach循环遍历集合中的元素。

当您输入该foreach循环时,您的集合中没有任何元素,它只是一个初始化为6的空数组,因此不会有任何对象添加到您的数组中。

另外,请注意,即使您在数组中有元素, foreach循环也不会分配它们:

 o = new MyObject(); 

基本上意味着分配给一个新的MyObject实例,但是o本身不是数组objects一部分,它只是一个用于迭代数组元素的临时容器,但在这种情况下,没有。

只有在显式声明要克隆对象时才会“复制”对象(此对象显式实现克隆function)。

你似乎混淆了引用和名称。

在第一个示例中,在foreach中,local o变量引用存储区域,存储来自objects一些对象。 当你正在做o = new MyObject() ,在一些其他内存区域中初始化一个新的MyObject,然后重写o引用以指向这个新的内存区域。

在第二个例子中,通过编写objects[i] = new MyObject() ,你说的是objects[i]引用应该被重写,而不是一些本地o变量。

我要提到的第一件事是非零长度数组总是可变的。并且在foreach循环内部

 for(MyObject o in objects) 

它在每次迭代中的作用如下。

 o = objects[0] // first iteration o = objects[1] // 2nd iteration 

但在您的情况下,您将另一个对象分配给引用o。 不是数组中的对象。它就像下面一样。

 ObjeMyObject objects[] = new MyObject[6]; MyObject o = Object[0]; 0 = new MyObject(); 

但是你的原始对象[0]仍然指向一个空对象。

每次使用“new”运算符时,JVM都会创建一个新实例,它将被赋值给赋值运算符的左侧操作数。 对于每个循环或循环都无关紧要。 对于(MyObject O:Object)的每个循环,只创建一次将是MyObject的O,它将不会被实例化,而Object数组中的值将保持在O中复制

 O = Object[0] O = Object[1] O = Object[2] O = Object[3] O = Object[4] O = Object[5] 

我们不需要关注增加计数器,这就是每个循环的美感。