java 意外的空指针异常()

c2e8gylq  于 2023-03-16  发布在  Java
关注(0)|答案(1)|浏览(143)

刚刚开始熟悉java -我在这里有点困惑,我有一个简单的控制器与以下代码

package com.ajaxict.edhub;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;

import java.io.IOException;

public class WelcomeController {

    public Stage stage;
    private Scene scene;
    private Parent root;

    public void switchToWelcome(ActionEvent event) throws IOException {
        root = FXMLLoader.load(getClass().getResource("welcome_view.fxml"));
        stage = (Stage)((Node)event.getSource()).getScene().getWindow();
        scene = new Scene(root);
        stage.setTitle("EdHub - Welcome");
        stage.setScene(scene);
        stage.show();
    }

    public void switchToRegister(ActionEvent event) throws IOException {
        Parent root = FXMLLoader.load(getClass().getResource("register_view.fxml"));
        stage = (Stage)((Node)event.getSource()).getScene().getWindow();
        scene = new Scene(root);
        stage.setTitle("EdHub - Register");
        stage.setScene(scene);
        stage.show();
    }

    public void quit_frame (ActionEvent event) {
    // stage = (Stage)((Node)event.getSource()).getScene().getWindow();
      stage.close(); 
    }

}

从上面的代码中,我相信stage属性会在我调用switchToWelcome()或switchToRegister()方法时被初始化-因此,如果我稍后调用quit_frame()方法,也不会出现任何问题,因为这个stage属性可以通过这个类访问。但每次我尝试调用quit_frame()方法时,仍然会得到NullPointerException
我在quit_frame()方法中添加了现在已注解的行,即在调用close()方法之前重新初始化该阶段,一切正常。但我觉得我不需要这一行,因为该阶段已使用我最初调用的switchToRegister()初始化。
下面是批量错误消息的最后部分

Caused by: java.lang.NullPointerException: Cannot invoke "javafx.stage.Stage.close()" because "this.stage" is null
at com.ajaxict.edhub/com.ajaxict.edhub.WelcomeController.quit_app(WelcomeController.java:45)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
... 53 more

我尝试在quit_frame()方法中重新初始化stage属性,它可以工作,但我不认为这是最好的主意
下面是一个可重现的示例:WelcomeMain.java

package com.ajaxict.edhub;

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

public class WelcomeMain extends Application {

    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(WelcomeMain.class.getResource("welcomeView.fxml"));
    //  Scene scene = new Scene(fxmlLoader.load(), 320, 240);
        Scene scene = new Scene(fxmlLoader.load());
        stage.setTitle("EdHub - Welcome");
        stage.setScene(scene);
        stage.show();

    }

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

我的欢迎视图.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="309.0" prefWidth="543.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ajaxict.edhub.WelcomeController">
   <children>
      <TextField fx:id="txt_email" layoutX="241.0" layoutY="147.0" prefHeight="35.0" prefWidth="263.0" />
      <TextField fx:id="txt_pword" layoutX="241.0" layoutY="192.0" prefHeight="35.0" prefWidth="263.0" />
      <Button fx:id="btn_login" layoutX="426.0" layoutY="258.0" mnemonicParsing="false" prefHeight="27.0" prefWidth="78.0" text="Log In" />
      <Label layoutX="45.0" layoutY="147.0" prefHeight="35.0" prefWidth="141.0" text="Email Address">
         <font>
            <Font size="17.0" />
         </font>
      </Label>
      <Label layoutX="45.0" layoutY="182.0" prefHeight="35.0" prefWidth="109.0" text="Password">
         <font>
            <Font size="17.0" />
         </font>
      </Label>
      <Label alignment="CENTER" layoutX="23.0" layoutY="22.0" prefHeight="43.0" prefWidth="499.0" text="EdHub ">
         <font>
            <Font name="Monotype Corsiva" size="52.0" />
         </font>
      </Label>
      <Button fx:id="btn_register" layoutX="138.0" layoutY="258.0" mnemonicParsing="false" onAction="#switchToRegister" prefHeight="27.0" prefWidth="78.0" text="Register" />
      <Button fx:id="btn_quit" layoutX="45.0" layoutY="258.0" mnemonicParsing="false" onAction="#quit_app" prefHeight="27.0" prefWidth="78.0" text="Quit" />
   </children>
</AnchorPane>

我的注册表视图.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="487.0" prefWidth="564.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ajaxict.edhub.WelcomeController">
   <children>
      <TextField fx:id="txt_email" layoutX="248.0" layoutY="205.0" prefHeight="35.0" prefWidth="263.0" />
      <Button fx:id="btn_login" layoutX="142.0" layoutY="420.0" mnemonicParsing="false" onAction="#switchToWelcome" prefHeight="27.0" prefWidth="78.0" text="Log In" />
      <Label layoutX="52.0" layoutY="205.0" prefHeight="35.0" prefWidth="141.0" text="Lastname">
         <font>
            <Font size="17.0" />
         </font>
      </Label>
      <Label layoutX="50.0" layoutY="338.0" prefHeight="35.0" prefWidth="109.0" text="Password">
         <font>
            <Font size="17.0" />
         </font>
      </Label>
      <Label alignment="CENTER" layoutX="33.0" layoutY="19.0" prefHeight="43.0" prefWidth="499.0" text="EdHub ">
         <font>
            <Font name="Monotype Corsiva" size="52.0" />
         </font>
      </Label>
      <Button fx:id="btn_register" layoutX="433.0" layoutY="420.0" mnemonicParsing="false" prefHeight="27.0" prefWidth="78.0" text="Register" />
      <Button fx:id="btn_quit" layoutX="50.0" layoutY="420.0" mnemonicParsing="false" onAction="#quit_app" prefHeight="27.0" prefWidth="78.0" text="Quit" />
      <Label layoutX="52.0" layoutY="292.0" prefHeight="35.0" prefWidth="109.0" text="Gender">
         <font>
            <Font size="17.0" />
         </font>
      </Label>
      <ComboBox layoutX="247.0" layoutY="297.0" prefHeight="35.0" prefWidth="263.0" />
      <TextField fx:id="txt_email1" layoutX="248.0" layoutY="250.0" prefHeight="35.0" prefWidth="263.0" />
      <Label layoutX="52.0" layoutY="250.0" prefHeight="35.0" prefWidth="141.0" text="Email Address">
         <font>
            <Font size="17.0" />
         </font>
      </Label>
      <TextField fx:id="txt_email2" layoutX="251.0" layoutY="160.0" prefHeight="35.0" prefWidth="263.0" />
      <Label layoutX="55.0" layoutY="160.0" prefHeight="35.0" prefWidth="141.0" text="Firstname">
         <font>
            <Font size="17.0" />
         </font>
      </Label>
      <PasswordField layoutX="245.0" layoutY="343.0" prefHeight="35.0" prefWidth="263.0" />
   </children>
</AnchorPane>

我的welcomeController.java

package com.ajaxict.edhub;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import java.io.IOException;

public class WelcomeController {

    public Stage stage;
    private Scene scene;
    private Parent root;

    public void switchToWelcome(ActionEvent event) throws IOException {
        root = FXMLLoader.load(getClass().getResource("welcomeView.fxml"));
        stage = (Stage)((Node)event.getSource()).getScene().getWindow();
        scene = new Scene(root);
        stage.setTitle("EdHub - Welcome");
        stage.setScene(scene);
        stage.show();
    }

    public void switchToRegister(ActionEvent event) throws IOException {
        Parent root = FXMLLoader.load(getClass().getResource("registerView.fxml"));
        stage = (Stage)((Node)event.getSource()).getScene().getWindow();
        scene = new Scene(root);
        stage.setTitle("EdHub - Register");
        stage.setScene(scene);
        stage.show();
    }

    public void quit_app (ActionEvent event) {
//   stage = (Stage)((Node)event.getSource()).getScene().getWindow();
        this.stage.close();
    }

}
qv7cva1a

qv7cva1a1#

示例变量stage仅在调用switchToWelcomeswitchToRegister时在每个控制器中初始化。但在这两种情况下,您都要立即用从FXML文件加载的视图替换当前视图,这将创建一个新的控制器。stage变量不会在该新控制器示例中初始化。因此,无论何时调用quitApp()stage在调用它的示例上为空。
对于为不同FXML文件创建的控制器,使用相同的控制器类是一个不好的主意,因为这会直接导致引起此错误的那种混淆。每个FXML文件都应有自己的控制器类。
要获取对当前窗口的引用,直接从该窗口中显示的节点获取要干净得多,可以用通常的方式将其注入控制器。
比如说你可以

@FXML
private Parent root ;

然后

@FXML
private void quitApp() {
    root.getScene().getWindow().hide();
}

这将关闭当前窗口。如果你真的想关闭整个应用程序(而不仅仅是关闭当前窗口,这可能会也可能不会退出应用程序),你当然可以用Platform.exit()来代替。
下面是使用这种方法的应用程序版本。
请注意,fx:id添加到了FMXL的根元素中。
WelcomeView.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>

<AnchorPane fx:id="root" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="309.0" prefWidth="543.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ajaxict.edhub.WelcomeController">
   <children>
      <TextField fx:id="txtEmail" layoutX="241.0" layoutY="147.0" prefHeight="35.0" prefWidth="263.0" />
      <TextField fx:id="txtPword" layoutX="241.0" layoutY="192.0" prefHeight="35.0" prefWidth="263.0" />
      <Button fx:id="btnLogin" layoutX="426.0" layoutY="258.0" mnemonicParsing="false" prefHeight="27.0" prefWidth="78.0" text="Log In" />
      <Label layoutX="45.0" layoutY="147.0" prefHeight="35.0" prefWidth="141.0" text="Email Address">
         <font>
            <Font size="17.0" />
         </font>
      </Label>
      <Label layoutX="45.0" layoutY="182.0" prefHeight="35.0" prefWidth="109.0" text="Password">
         <font>
            <Font size="17.0" />
         </font>
      </Label>
      <Label alignment="CENTER" layoutX="23.0" layoutY="22.0" prefHeight="43.0" prefWidth="499.0" text="EdHub ">
         <font>
            <Font name="Monotype Corsiva" size="52.0" />
         </font>
      </Label>
      <Button fx:id="btnRegister" layoutX="138.0" layoutY="258.0" mnemonicParsing="false" onAction="#switchToRegister" prefHeight="27.0" prefWidth="78.0" text="Register" />
      <Button fx:id="btnQuit" layoutX="45.0" layoutY="258.0" mnemonicParsing="false" onAction="#quitApp" prefHeight="27.0" prefWidth="78.0" text="Quit" />
   </children>
</AnchorPane>

WelcomeController.java:

package com.ajaxict.edhub;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import java.io.IOException;

public class WelcomeController {

    @FXML
    private Parent root;

    public void switchToWelcome() throws IOException {
        Parent newRoot = FXMLLoader.load(getClass().getResource("welcomeView.fxml"));
        this.root.getScene().setRoot(newRoot);
    }

    public void switchToRegister() throws IOException {
        Parent newRoot = FXMLLoader.load(getClass().getResource("registerView.fxml"));
        this.root.getScene().setRoot(newRoot);
    }

    public void quitApp() {
        this.root.getScene().getWindow().hide();
    }

}

(我真的不明白为什么你的“欢迎”屏幕有“切换到欢迎”功能,但我把它留在了里面。)
registerView.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ComboBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>

<AnchorPane fx:id="root" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="487.0" prefWidth="564.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ajaxict.edhub.RegisterController">
   <children>
      <TextField fx:id="txtEmail" layoutX="248.0" layoutY="205.0" prefHeight="35.0" prefWidth="263.0" />
      <Button fx:id="btnLogin" layoutX="142.0" layoutY="420.0" mnemonicParsing="false" onAction="#switchToWelcome" prefHeight="27.0" prefWidth="78.0" text="Log In" />
      <Label layoutX="52.0" layoutY="205.0" prefHeight="35.0" prefWidth="141.0" text="Lastname">
         <font>
            <Font size="17.0" />
         </font>
      </Label>
      <Label layoutX="50.0" layoutY="338.0" prefHeight="35.0" prefWidth="109.0" text="Password">
         <font>
            <Font size="17.0" />
         </font>
      </Label>
      <Label alignment="CENTER" layoutX="33.0" layoutY="19.0" prefHeight="43.0" prefWidth="499.0" text="EdHub ">
         <font>
            <Font name="Monotype Corsiva" size="52.0" />
         </font>
      </Label>
      <Button fx:id="btnRegister" layoutX="433.0" layoutY="420.0" mnemonicParsing="false" prefHeight="27.0" prefWidth="78.0" text="Register" />
      <Button fx:id="btnQuit" layoutX="50.0" layoutY="420.0" mnemonicParsing="false" onAction="#quitApp" prefHeight="27.0" prefWidth="78.0" text="Quit" />
      <Label layoutX="52.0" layoutY="292.0" prefHeight="35.0" prefWidth="109.0" text="Gender">
         <font>
            <Font size="17.0" />
         </font>
      </Label>
      <ComboBox layoutX="247.0" layoutY="297.0" prefHeight="35.0" prefWidth="263.0" />
      <TextField fx:id="txtEmail1" layoutX="248.0" layoutY="250.0" prefHeight="35.0" prefWidth="263.0" />
      <Label layoutX="52.0" layoutY="250.0" prefHeight="35.0" prefWidth="141.0" text="Email Address">
         <font>
            <Font size="17.0" />
         </font>
      </Label>
      <TextField fx:id="txtEmail2" layoutX="251.0" layoutY="160.0" prefHeight="35.0" prefWidth="263.0" />
      <Label layoutX="55.0" layoutY="160.0" prefHeight="35.0" prefWidth="141.0" text="Firstname">
         <font>
            <Font size="17.0" />
         </font>
      </Label>
      <PasswordField layoutX="245.0" layoutY="343.0" prefHeight="35.0" prefWidth="263.0" />
   </children>
</AnchorPane>

RegisterController.java:

package com.ajaxict.edhub;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import java.io.IOException;

public class RegisterController {

    @FXML
    private Parent root;

    public void switchToWelcome() throws IOException {
        Parent newRoot = FXMLLoader.load(getClass().getResource("welcomeView.fxml"));
        this.root.getScene().setRoot(newRoot);
    }

    

    public void quitApp () {
        this.root.getScene().getWindow().hide();
    }

}

显然,您可以通过使用委托或继承来删除控制器中的重复代码。

相关问题