[英]Local object creation in JAVA
我正在写一些我上学几天的JAVA作业,但遇到了很奇怪的事情(嗯,至少对我来说是……)。
由于该问题与我的项目无关,所以我写了一些代码来表达我要询问的行为,因此请忽略以下代码中可能遇到的与该特定问题无关的任何问题。
考虑以下类别:
package test;
import java.util.ArrayList;
import java.util.List;
public class Car {
List<Doors> doors;
int numOfDoors;
public Car(int numOfDoors) {
this.numOfDoors=numOfDoors;
}
public void prepare() {
run(doors);
}
public void run(List<Doors> listOfDoors) {
listOfDoors=new ArrayList<Doors>(numOfDoors);
}
public List<Doors> getDoors() {
return doors;
}
}
和这个:
package test;
import java.util.List;
public class TestDrive {
public static void main(String[] args) {
Car car=new Car(5);
car.prepare();
List<Doors> listOfDoors=car.getDoors();
if (listOfDoors==null) {
System.out.println("This is not the desired behaviour.");
}
else {
System.out.println("This is the desired behaviour.");
}
}
}
我同意,这有点愚蠢,没有意义,但是再次-我写它只是为了满足我的好奇心。
现在,您可能已经猜到了,输出为“这不是所需的行为。”,这意味着,即使在“ run()”方法中为其分配了新的对象,“门”字段仍保留一个空指针。 所以我的问题是为什么? 为什么它为空?
我的意思是,我知道,创建局部变量(可能是原始变量,对象或对对象的引用)会导致在我们离开方法范围时将其丢失,但事实并非如此,因为是对那个新创建的对象(门)的实时引用,那么JAVA为什么会销毁它?
让我们逐步分析它。
Car car=new Car(5);
car.prepare();
您创建了一辆新车,并在prepare
之后希望它有5门车的清单。 现在让我们看看prepare
期间发生了什么。
public void prepare() {
run(doors);
}
public void run(List<Doors> listOfDoors) {
listOfDoors=new ArrayList<Doors>(numOfDoors);
}
new ArrayList
被分配给局部变量 listOfDoors
。 当你写run(doors)
你不传递一个指针变量doors
,你可能期望在C情况下,例如。 您正在将null
(因为尚未初始化doors
)传递给run方法。
运行开始时,我们有
List<Doors> listOfDoors = null;
这是在调用时传递null
引用的结果。 然后,为该局部变量分配一个新列表,仅在方法终止时销毁。 如您所见, doors
没有分配任何东西,从而导致了意外的行为。
要解决此问题,请删除run方法,然后重写您的prepare方法。
public void prepare() {
doors = new ArrayList<Doors>(numOfDoors);
}
这样,您应该会获得预期的行为。
有两件事:
通过参数作为参考
将参数传递为价值
在Java中,您只能将参数作为值传递-这意味着方法run
会获取doors
引用的副本(是的-可能会造成混淆)。 因此,现在您有了reference
doors
并且此参考的副本名为listOfDoors
。 但是它什么也没指。 所以当你写
listOfDors = new ArrayList<Doors>();
现在listOfDoors
引用了门的arraylist,但是可变doors
仍然不引用任何内容。
因此,顺便说一句-如果在传递该引用的副本之前初始化了doors
对象,则可以编写:
listOfDoors.add(new Door());
这将与
doors.add(new Door());
尽管有两个不同的引用,但它们将引用同一个对象。
问题在这里:
public void run(List<Doors> listOfDoors) {
listOfDoors = new ArrayList<Doors>(numOfDoors);
}
它应该是:
public void run(List<Doors> listOfDoors) {
doors = new ArrayList<Doors>(numOfDoors);
}
了解有关参数传递的信息 。
在检查传递给构造函数的门数之后,您需要在Car
构造函数中初始化Doors
。 另外, run()
需要访问this.doors
而不是访问本地门(方法参数)。
当您分配listOfDoors=new ArrayList<Doors>(numOfDoors);
在运行中,您不会改变doors
,因为它是不同的参考。 调用run(doors)
创建一个新的引用该对象doors
是指,你,然后制定和无所事事时(本参考失去范围run
退出)。 doors
参考永远不会设置为您正在run
创建的对象。
该方法run
可能会将您现有的doors
作为参数,但是在函数中,参数doors
被新列表覆盖(它不是具有不同内容的同一对象)。
因此,原始的doors
对象仍然为null
。
Java不是通过引用传递的。 所以
public void run(List<Doors> listOfDoors) {
listOfDoors=new ArrayList<Doors>(numOfDoors);
}
不更新与run(doors)
的变量doors
传入的列表相同的列表。 相反,run方法将分配给新的引用,因此不会设置可变doors
。
这也是为什么通常的swap方法在Java中不起作用的原因
你是在错误的方向想转让的,通过将门doors
到run
你有被声明为方法的参数和一个新的变量/引用是局部的,你再初始化它,它只能活只要该方法执行即可。 可变doors
保持不变。 它与C
或C++
中的不一样,在C
或C++
,您可以传递指针并将值分配给它指向的对象。
您需要初始化doors
,在构造函数中应该是一个好地方:
public Car(int numOfDoors) {
this.numOfDoors=numOfDoors;
this.doors = new ArrayList<>(this.numOfDoors);
}
而且也没有必要-至少如果你的意图是,该方法run
工作的成员变量doors
-一个引用传递给它的那个方法,只是写:
public void run() {
// user this.doors here
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.