You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

IntelliJ 2018.1与Scene Builder 9.0.1下JavaFX切换场景空指针问题

解决JavaFX中Controller调用Main切换场景时的NullPointerException问题

你的空指针报错原因非常清晰:你在Controller里手动创建了一个新的Main实例,但这个实例的primaryStage根本没有被初始化。JavaFX的start()方法是由FX应用启动器自动调用的,你自己new出来的Main对象不会执行start()方法,所以它的primaryStage变量还是null,调用changeScene()自然就触发空指针了。

下面给你几种可行的解决方案,按推荐程度排序:

方案1:从当前控件获取Stage(最推荐,解耦)

不需要依赖Main类,直接从触发点击事件的按钮获取它所在的Stage,这种方式让Controller和Main完全解耦,代码更灵活:

修改Controller的buttonNewProductOnMouseClicked方法:

public void buttonNewProductOnMouseClicked(javafx.scene.input.MouseEvent mouseEvent) {
    buttonNewProduct.setText("You have clicked on me!");
    
    // 从按钮获取当前所在的Stage
    Stage stage = (Stage) buttonNewProduct.getScene().getWindow();
    
    try {
        // 加载目标FXML
        Parent pane = FXMLLoader.load(getClass().getResource("new_product/new_product.fxml"));
        Scene newScene = new Scene(pane);
        stage.setScene(newScene);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

方案2:给Controller传递Stage引用

在加载主FXML的时候,把Main中初始化好的primaryStage传递给Controller:

修改Main.java的start方法

@Override
public void start(Stage primaryStage) throws Exception{
    this.primaryStage = primaryStage;
    
    // 改用FXMLLoader实例加载,方便获取Controller
    FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
    Parent root = loader.load();
    
    // 获取Controller实例并传递Stage
    Controller controller = loader.getController();
    controller.setStage(primaryStage);
    
    Scene primaryScene = new Scene(root, 300, 275);
    primaryStage.setScene(primaryScene);
    primaryStage.show();
}

修改Controller.java

添加Stage成员变量和setter方法,然后直接用这个Stage切换场景:

package sample;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.stage.Stage;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import java.io.IOException;

public class Controller {
    @FXML
    private Button buttonProducts;
    @FXML
    private Button buttonNewProduct;
    
    private Stage stage;

    // 接收Main传递的Stage
    public void setStage(Stage stage) {
        this.stage = stage;
    }

    public void buttonProductsOnMouseClicked(javafx.scene.input.MouseEvent mouseEvent) {
        buttonProducts.setText("You have clicked on me!");
    }

    public void buttonNewProductOnMouseClicked(javafx.scene.input.MouseEvent mouseEvent) {
        buttonNewProduct.setText("You have clicked on me!");
        
        try {
            Parent pane = FXMLLoader.load(getClass().getResource("new_product/new_product.fxml"));
            Scene newScene = new Scene(pane);
            stage.setScene(newScene);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

方案3:将Main设为单例(不推荐,强耦合)

如果你一定要通过Main类来切换场景,可以把Main做成单例,确保Controller拿到的是启动时的那个实例:

修改Main.java

package sample;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;

public class Main extends Application {
    private static Main instance;
    private Stage primaryStage;

    @Override
    public void start(Stage primaryStage) throws Exception{
        instance = this; // 保存启动时的实例
        this.primaryStage = primaryStage;
        
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        Scene primaryScene = new Scene(root, 300, 275);
        primaryStage.setScene(primaryScene);
        primaryStage.show();
    }

    // 提供获取单例的静态方法
    public static Main getInstance() {
        return instance;
    }

    public void changeScene(String fxml){
        Parent pane = null;
        try {
            pane = FXMLLoader.load(getClass().getResource(fxml));
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 加非空判断,避免FXML加载失败导致的空指针
        if(pane != null) {
            Scene scene = new Scene(pane);
            primaryStage.setScene(scene);
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

修改Controller的调用

public void buttonNewProductOnMouseClicked(javafx.scene.input.MouseEvent mouseEvent) {
    buttonNewProduct.setText("You have clicked on me!");
    Main.getInstance().changeScene("new_product/new_product.fxml");
}

额外注意事项

  • 确认new_product.fxml的路径正确:你写的"new_product/new_product.fxml"需要对应项目中src/sample/new_product/目录下的文件(编译后会在classpath的对应位置)。
  • 加载FXML时建议添加异常处理和非空判断,避免因为文件找不到或加载失败导致后续错误。

内容的提问来源于stack exchange,提问作者Sigma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:23:07