我在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()
方法中,通过引用主阶段来调用Initialize。
然而,当我稍后调用getInstance()
然后尝试切换屏幕时,我失败了NullPointerException
,因为mainStage
为空。看起来该领域在它首次使用和它的后续使用之间变得无效。怎么样?
为什么会这样?
答案 0 :(得分:2)
您永远不会初始化instance
,因此您的getInstance()
方法每次都会返回一个新对象(它几乎与单例相反;您已经很难同时使用同一个实例...)。
你需要
public static ScreenManager getInstance() {
if (instance == null ) {
instance = new ScreenManager();
}
return instance;
}
只是一些评论:许多程序员不鼓励使用单例模式,因为它有很多问题。您可以考虑使用依赖注入。此外,由于此单例的主要目的似乎是提供对舞台的访问,请注意您可以通过Node.getScene().getWindow()
获取对舞台的任何节点的引用(如果您可能需要向下转换结果)需要Stage
- 特定功能)。由于控制器始终可以访问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(...);
等
总的来说,这种方法有一些优点,而不是直接实现它:首先,这是立即线程安全的,而不是我在本文顶部发布的解决方案。