【问题标题】:Field in singleton unexpectedly null - Why?单例中的字段意外为空 - 为什么?
【发布时间】:2018-04-08 12:01:06
【问题描述】:

我在一个 JavaFX 程序中有以下单例,目的是为了更容易在应用程序的不同屏幕之间切换:

public class ScreenManager() {

    private Stage mainStage;

    private static ScreenManager instance;

    private ScreenManager() {
        // TODO
    }

    public static ScreenManager getInstance() {
        if (instance == null ) {
            return new ScreenManager();
        } else {
            return instance;
        }
    }

    public void initialize(Stage mainStage) {
        this.mainStage = mainStage;
    }

    public void switchToScreen(String fxmlPath) {
        Parent newScreenRoot;

        try {
            URL pathToFxml = getClass().getResource(fxmlPath);
            newScreenRoot = fxmlLoader.load(pathToFxml);
        } catch (IOException e) {
            throw new IllegalArgumentException("Failed to load FXML", e);
        }

            Scene newScreen = new Scene(newScreenRoot);
            mainStage.setScene(newScreen);
            mainStage.setMaximized(true);
        }

}

在 JavaFX 的 start() 方法期间,通过对主阶段的引用调用初始化。

但是,当我稍后调用getInstance() 然后尝试切换屏幕时,我失败了NullPointerException,因为mainStage 为空。似乎该字段在第一次使用和后续使用之间变为空。怎么样?

为什么会这样?

【问题讨论】:

  • 你永远不会初始化instance...
  • 天哪。我已经做这整个编程工作 6 年了,但不知何故错过了这一点。我在调试器和所有东西中运行了它。这就是你每晚睡 4 小时时会发生的情况。
  • 现在很多程序员都建议不要使用单例;您可以考虑使用依赖注入解决方案。
  • 我考虑过,但我现在正在与一个非常绿色(学生)团队合作,如果我尝试将他们介绍给 DI 和 IoC,他们可能会融化。
  • FWIW 一位与我一起工作的研究生最近问我编程了多长时间。我必须计算一下……我很震惊地发现已经 38 年了。所以你在我的书中仍然是一个新手,如果它能让你感觉更好:)。

标签: java javafx nullpointerexception singleton


【解决方案1】:

您永远不会初始化 instance,因此您的 getInstance() 方法每次都会返回一个新对象(这几乎与单例相反;您已经很难多次使用同一个实例...) .

你需要

public static ScreenManager getInstance() {
    if (instance == null ) {
        instance = new ScreenManager();
    } 
    return instance;
}

只有几个 cmets:许多程序员不鼓励使用单例模式,因为它存在许多问题。您可以考虑改用依赖注入。此外,由于此单例的主要目的似乎是提供对阶段的访问权限,请注意,您可以通过Node.getScene().getWindow() 获取对包含任何节点的阶段的引用(如果需要,您可能需要向下转换结果@ 987654325@ 特定功能)。由于控制器始终可以访问 UI 层次结构中的某个节点,因此您可能根本不需要它。

最后,如果您确实决定需要/想要使用单例,实现单例模式的另一种方法是使用只有一个值的枚举:

import java.io.IOException;
import java.net.URL;

import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public enum ScreenManager {

    INSTANCE ;

    private Stage mainStage;


    public void initialize(Stage mainStage) {
        this.mainStage = mainStage;
    }

    public void switchToScreen(String fxmlPath) {
        Parent newScreenRoot;

        try {
            URL pathToFxml = getClass().getResource(fxmlPath);
            newScreenRoot = FXMLLoader.load(pathToFxml);
        } catch (IOException e) {
            throw new IllegalArgumentException("Failed to load FXML", e);
        }

            Scene newScreen = new Scene(newScreenRoot);
            mainStage.setScene(newScreen);
            mainStage.setMaximized(true);
        }

}

然后你可以做类似的事情

ScreenManager.INSTANCE.initialize(primaryStage);
ScreenManager.INSTANCE.switchToScreen(...);

等等

总的来说,与直接实现相比,这种方法有一些优势:首先,这是立即线程安全的,而我在本文顶部发布的解决方案并非如此。

【讨论】:

  • 叹息......有时我想知道这个工程学位是否对我有好处,哈哈
猜你喜欢
  • 2015-12-22
  • 1970-01-01
  • 1970-01-01
  • 2016-08-29
  • 1970-01-01
  • 2022-01-04
相关资源
最近更新 更多