繁体   English   中英

在JAVA中创建本地对象

[英]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中不起作用的原因

你是在错误的方向想转让的,通过将门doorsrun你有被声明为方法的参数和一个新的变量/引用是局部的,你再初始化它,它只能活只要该方法执行即可。 可变doors保持不变。 它与CC++中的不一样,在CC++ ,您可以传递指针并将值分配给它指向的对象。

您需要初始化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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM