【问题标题】:New ArrayList Objects Disappear When Method Exits方法退出时新的 ArrayList 对象消失
【发布时间】:2018-08-21 19:13:45
【问题描述】:

您好,提前感谢您。我在这里相对较新,到目前为止,我已经能够解决我在搜索中遇到的任何问题,但这个问题让我很难过。

我正在编写一个程序,该程序使用 Rest-Assured 进行 API 调用以获取云服务器中不同对象的配置文件。这些对象是我们可以称之为文件夹的组织文件容器。这些文件夹具有特定的结构,每个文件夹都有特定的元数据配置文件。我的程序所做的是从返回的 JSON 创建 java 对象,并以允许它们仍然像文件夹结构一样工作的方式存储它们,以便可以操作它们来执行以下操作:打印文件夹结构,获取特定的子文件夹文件夹、查看特定文件夹的名称或 ID、添加或删除文件夹等。

我已将其设置为每个文件夹都是一个对象,其中包含其子文件夹的 ArrayList 和元数据变量,其中构造函数将根据 JSON 填充所有变量。

在使用我的递归方法调用 API 并将子文件夹添加到它们应该去的位置之后,在类内部的方法中,我可以看到它们已正确添加,一旦方法退出,如果我尝试访问来自 Main 的列表我得到一个空指针异常。请参阅下面的代码示例。

编辑:我在下面的代码中添加了额外的测试,以检查递归方法中的数组索引是否为空。创建对象时,构造函数使用indexOf() 打印 ArrayList 中的文件夹 ID 和索引。当它从构造函数返回时,在相同的递归方法中,getSingleFolder(index).getSubfolders().get(0).getSubfolders().get(0).toString 仍然为 null,即使这应该返回刚刚从构造函数中创建并添加到父文件夹 ArrayList(并已验证)的同一文件夹。

        public void saveSubfoldersRecursively(Folder folder){
        if (folder.hasSubfolders()){
            getFolderSubfoldersJsonArrayFromRestApi(folder);

            for(int x = 0; x < folder.getFoldersJsonArray().size(); x++) {
                JsonObject currentSubfolderJson = folder.getFoldersJsonArray().get(x).getAsJsonObject();
                JsonObject fullSubfolderProfileObject = getFolderProfileFromRestApi(currentSubfolderJson);
                Folder newSubfolder = folder.addSubfolder(fullSubfolderProfileObject);

                if (newSubfolder.hasSubfolders()) {
                    saveSubfoldersRecursively(newSubfolder);
                }
            }
            if (folder.getId().equals("<folderID>") ) {
                System.out.println("inside saveSubfoldersRecursively");  // this prints fine
// Null Pointer Exception at next line!
                System.out.println("access test 1: " + folder.getId() + " subfolder at index 0: " + folder.getSubFolders().get(0)); 
        }
    }

根据要求,getFolderProfileFromRestApi()getFoldersJsonArrayFromRestApi() 方法只需使用 Rest-Assured 进行调用并返回一个 GSON JsonObject,其中包含构造函数所需的信息或它们的功能所需的方法,我已经使用过这些有点,并已验证它们按预期工作。调用folder.addSubfolder() 调用文件夹构造函数并返回新文件夹,以便它可以用于获取它自己的文件夹等。我已经非常广泛地使用了它并验证它确实返回了正确的信息并且构造函数被调用.如果我将 System.out 检查放在实际的 folder.addSubfolder() 方法中,我可以访问数组中的元素,因为它们仍然存在,但当控制返回到 main 时似乎消失了。

Folder 类中的 addSubfolder 方法如下所示。我添加了系统输出来检查并确保一切都在发生,实际上这些都添加到了它们应该添加的位置。这里的输出完全符合预期。

public Folder addSubfolder(JsonObject folderProfile){
    System.out.println("Adding " + folderProfile.get("id") + " to subfolders of " + this.getId());        //this is a check to be sure the folderProfile passed has the right folderID and has been passed ot the right parent folder. this also returns the correct info in all cases
    Folder subfolder = new Folder(folderProfile);
    this.subFolders.add(subfolder);
    System.out.println("subfolder added at index " + this.subFolders.indexOf(subfolder));    //This is just a check to see that the subfolder is now in the array and it does return the correct index. 
    return subfolder;
}

当我返回 Main 时,我编写了一个小型递归方法,仅使用名称以标准格式打印结构,并且它似乎可以工作,直到它到达一个有 3 级的地方,然后它当它尝试访问该级别的第一个元素时抛出空指针异常。

相反,我使用特定代码为刚刚创建并在 addSubfolder 方法中检查的确切嵌套子文件夹调用 toString,它还返回空指针异常。即

server.getSinglefolder(1).getSubFolders().get(0).getSubFolders().get(0).toString();

据我所知,这是另一个 StackOverflow 帖子

Objects disappearing of ArrayList

有同样的问题,但我似乎无法理解如何根据我的情况转换解决方案,或者它是否不起作用,因为我不应该这样做。感谢您的帮助!

编辑:我再次查看了该帖子,似乎他的问题是他后来的代码正在更改它,但他没有注意到,因为它是一个参考。我不认为这是这种情况,因为在该方法返回后,该对象不会再次被访问,直到它在 main 中调用并返回为空。我现在整天都在搜索和阅读文章,这一定是我看不到的愚蠢的东西。再次感谢您的帮助。

编辑 2:经过进一步测试,我注意到添加到 ArrayList 的文件夹可以在构造函数中使用this 获取对象并调用获取器的 ID 来验证。但是,当从调用构造函数的递归方法的末尾访问时,ArrayList 在该索引处为空(添加到代码示例的 System.out 测试)。因此,实际上,只要构造函数返回,arraylist 在控制甚至返回到 main 之前再次为 Null。这可能与我在方法中声明占位符对象(文件夹、子文件夹、newSubfolder)并因此在退出时被清除的事实有关吗?即使是这种情况,即使临时引用消失,对象本身仍应保存更改。

编辑 3:根据要求,这里是 folder.hasSubFolders() 和文件夹构造函数的方法。

private boolean hasSubfolders;


    public boolean hasSubfolders() {
    return hasSubfolders;
}

从 addSubfolder 中调用的文件夹构造函数。请注意,整个构造函数大约 150 行,但格式相同,检查 JSON 中的成员,如果存在,将值保存到类变量。如果有人真的认为有必要,我可以发布整个内容。

 //main constructor, parses response string to JSON and pulls all data to fill class variables.
public Folder(JsonObject folderProfile) {

    if (folderProfile.has("database")) {
        this.database = folderProfile.get("database").getAsString();
    }

    if (folderProfile.has("default_security")) {
        this.defaultSecurity = folderProfile.get("default_security").getAsString();
        }
...}

编辑 5:程序每个部分的 ideone 链接。它当然不会在没有访问其余服务器的情况下运行,但不幸的是我无法共享连接信息。我可以向您保证,其余调用正常运行并准确返回其应有的数据,并且所有这些数据都得到正确处理(以一种可行的方式正确处理,但可能不是最佳实践)-_-

注意:我知道我需要清理抽象类及其实现方式,我还有很多工作要做来清理编码实践,但这远未完成,只有一半-完成了我正在测试的程序。但是,由于我一直在学习,因此我完全愿意接受该领域的任何建议。谢谢

主要 - https://ideone.com/Y2FYrm

IMContainer - https://ideone.com/TFdiqk

文件夹 - https://ideone.com/gX4nda

模板 - https://ideone.com/7VfkPX

服务器 - https://ideone.com/geJazD

【问题讨论】:

  • 详细问题值得点赞
  • 这条语句“server.getSinglefolder(1).getSubFolders().get(0).getSubFolders().get(0).toString();”是一条长链。你能知道它在哪个点表示法上引发了空指针异常吗?
  • 谢谢@Mr.Brickowski!我同意它有点长而且令人费解,但在这种情况下,我只能检查那里是否有元素。我在它上面添加了相同的字符串( legendDev.getSingleTemplate(1).getSubFolders().get(0).toString(); 并取消了一个级别,因为在递归方法进入之前添加了第一级并且它确实打印和不会在那里抛出异常。我怀疑我的递归有问题。奇怪的是,如果从递归方法中执行,上面的字符串仍然有效,它只有在方法退出时才为 null。
  • 有些领域是未知的。你能提供 Folder() 构造函数和 Folder.hasSubfolders() 实现吗?另外,整个系统可以同时运行吗?即线程安全类型的东西。
  • @Mr.Brickowski 感谢您一直以来的关注和帮助!我真的在这里绞尽脑汁。正如我可能已经提到的那样,我对编写超出课堂和教科书练习的功能的 Java 程序还比较陌生,所以我不确定我是否熟悉线程安全,但我会做我的研究。非常感谢您可以指出我的任何资源。至于并发运行,整个程序由 Main/psvm 控制,只需几行即可运行服务器方法进行连接、添加子文件夹然后尝试打印它们。

标签: java arraylist


【解决方案1】:

我怀疑当您递归地将文件夹添加到您的文件夹 arrayList 时,您并没有将子文件夹添加到添加的子文件夹中。这会创建一个场景,其中第三次递归的子文件夹会引发 NullPointerException。如果这是唯一的问题,请移动

if (newSubfolder.hasSubfolders()) {
    saveSubfoldersRecursively(newSubfolder);
}

到方法的开头可能会有所帮助。

但是,在不知道实现的情况下

getFolderSubfoldersJsonArrayFromRestApi

getFolderProfileFromRestApi

我不能肯定。

【讨论】:

  • 非常感谢您的帮助,REST API 首先返回一个数组,其中包含表示文件夹配置文件的元素,但由于此 API 的限制,这些元素缺少关键元数据。我所做的是将完整数组作为单独的 JsonArray 保存到对象中,然后使用该列表调用 API 以获取每个文件夹的完整配置文件。 getFolderProfileFromRestApi 只是一个 Rest-Assured 调用,它返回一个 JsonObject,其中包含构造函数所需的完整配置文件。在这种情况下,我必须首先从 REST 响应中创建 newFolder 对象。
  • 还值得注意的是,在类内部,在 addSubfolders 方法中,检查在添加后立即使用 .indexOf(subfolder) 时显示正确的索引,但稍后在 main 中访问时返回 null .据我所知,我已经正确添加了子文件夹,因为它们在执行过程中的某一时刻就在那里。
【解决方案2】:

我同意 Chi-Young Jeffrey Lii 的回答,这是对此的进一步阐述。 addSubfolder() 基本上做了3件事。

public Folder addSubfolder(JsonObject folderProfile){
     System.out.println("Adding " + folderProfile.get("id") + " to subfolders of " + this.getId());
     // 1.create new subfolder
     Folder subfolder = new Folder(folderProfile);
     // 2.add subfolder to this subFolders arraylist
     this.subFolders.add(subfolder);
     System.out.println("subfolder added at index " + this.subFolders.indexOf(subfolder));    
     // 3.return subfolder
     return subfolder;

}

如下图

语句执行时(高亮颜色与图表颜色匹配),蓝色部分为空ArrayList,如果调用.get(0),则返回null。如果你在一个空变量上调用toString(),它会抛出空指针异常。

由于我们无法访问所有代码,因此这是我们可以猜测出根本原因的最佳假设。


编辑 1

我认为你很困惑为什么在addSubfolder() 中获取索引有效,但是当你在saveSubfoldersRecursively() 中检查它时,它会给你错误。这是因为两个语句指向不同的引用。

System.out.println("access test 1: " + folder.getId() + " subfolder at index 0: " + folder.getSubFolders().get(0)); 

不等于

System.out.println("subfolder added at index " + this.subFolders.indexOf(subfolder));    

我敢打赌,如果你在addSubfolder() 的最后一行写下这个,也会给你例外。

System.out.println("subfolder's subfolder null check:" + subfolder.getSubFolders().get(0).toString());

编辑 2

对给出的源代码的解释。我发现文件夹有自己的子文件夹,模板也有自己的。它们是不同的文件夹列表,如何将子文件夹添加到文件夹类可以从模板中检索回来?

【讨论】:

  • 非常感谢您花时间绘制此图表。我查看了代码,我相信我在发布示例时并不清楚。您示例中的橙色文件夹实际上是模板的根级文件夹。在我的程序中,模板和文件夹都实现了容器的抽象类。所以实际上,当我运行程序时,还会再增加一层,导致所有颜色下降一个档次,并将所有内容排列到它工作的位置。这就是为什么它让我如此困惑的原因,因为我可以从 addsubfolder() 中访问这个数组和索引,但不能在之后
  • 我绝对可以发布整个程序,或者至少通过其他方式分享它,因为在这里发布可能很笨拙。有这样做的首选方法吗?只在编辑中发布 1000 行或其他内容似乎很懒。
  • 谢谢,我会将链接添加到当前问题的底部!
  • 我已将链接添加到主要问题,感谢您的建议,再次感谢您的帮助。
猜你喜欢
  • 2015-11-15
  • 1970-01-01
  • 2015-10-27
  • 1970-01-01
  • 2011-12-20
  • 1970-01-01
  • 2017-08-17
  • 1970-01-01
  • 2022-01-25
相关资源
最近更新 更多