我是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.");
}
}
}
我同意,这有点愚蠢而没有意义,但是再一次 - 我写这篇文章只是为了满足我的好奇心。
现在,正如您可能已经猜到的那样,输出是“这不是期望的行为。”,意思是,字段“doors”包含空指针,即使它在“run()中分配了一个新对象” “ 方法。所以我的问题是为什么?为什么它是空的?
我的意思是,我知道创建一个局部变量 - 可能是一个原始变量,一个对象或一个对象的引用 - 当我们离开方法的范围时会导致它失去它,但这不是这里的情况,因为那里是实时对新创建的对象(门)的引用,那么为什么JAVA会破坏它?
感谢所有通过并阅读整篇文章的人。
答案 0 :(得分:2)
让我们一步一步地分析它。
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
没有分配任何内容,导致意外行为。
要解决此问题,请删除运行方法并重写准备方法。
public void prepare() {
doors = new ArrayList<Doors>(numOfDoors);
}
有了这个,你应该得到预期的行为。
答案 1 :(得分:0)
有两件事:
传递参数作为参考
将参数传递为值
在Java中,您只能将参数传递为值 - 这意味着方法run
获取doors
引用的副本(是的 - 它可能会令人困惑)。现在,您已获得reference
doors
以及此引用的副本 - listOfDoors
。但它还没有提到。所以当你写
listOfDors = new ArrayList<Doors>();
现在listOfDoors
引用门的arraylist,但变量doors
仍然没有引用任何内容。
顺便说一句 - 如果在传递该引用的副本之前初始化了doors
对象,则可以写:
listOfDoors.add(new Door());
,它与
相同doors.add(new Door());
尽管这些是两个不同的引用,但它们会引用相同的对象。
答案 2 :(得分:0)
问题在于:
public void run(List<Doors> listOfDoors) {
listOfDoors = new ArrayList<Doors>(numOfDoors);
}
应该是:
public void run(List<Doors> listOfDoors) {
doors = new ArrayList<Doors>(numOfDoors);
}
答案 3 :(得分:0)
在检查传递给构造函数的门数后,您需要在Doors
构造函数中初始化Car
。
此外,run()
需要访问this.doors
而不是访问本地门(方法参数)。
答案 4 :(得分:0)
在运行中分配listOfDoors=new ArrayList<Doors>(numOfDoors);
时,您不会更改doors
,因为它是一个不同的参考。调用run(doors)
会创建一个对doors
所引用的对象的新引用,然后您将对其进行设置并且不执行任何操作(此引用在run
退出时会丢失范围)。 doors
引用永远不会设置为您在run
中创建的对象。
答案 5 :(得分:0)
方法run
可能将您现有的doors
作为参数,但在函数中,参数doors
会被新列表覆盖(它与不同的对象不同)含量)。
因此原始doors
对象仍为null
。
答案 6 :(得分:0)
Java不是通过引用传递的。所以
public void run(List<Doors> listOfDoors) {
listOfDoors=new ArrayList<Doors>(numOfDoors);
}
不更新doors
中作为变量run(doors)
传入的相同列表。相反,run方法会分配给新引用,因此永远不会设置变量doors
。
这也是通常的交换方法在java
中不起作用的原因答案 7 :(得分:0)
你正在考虑向错误的方向进行任务,通过将门传递给doors
到run
你有一个新的变量/引用,它被声明为该方法的参数,并且是本地的它,然后你初始化它,它只在方法执行时生存。变量doors
保持不变。它与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
}