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

JavaFX杂货清单GUI程序文件读写崩溃及显示异常求助

JavaFX杂货清单程序问题排查与修复建议

问题概述

我正在完成中期课程作业,开发一款基于JavaFX的GUI杂货清单程序,需求包含将清单内容写入并读取.txt文件,删除及批量删除功能暂未开发。测试中遇到以下问题:

  • 程序可将内容写入Grocery.txt,但ListView无法显示写入的内容
  • 调整代码后,点击Add按钮弹出无内容的错误提示框,将if (errorMsg == " ")改为if(errorMsg.isBlank())后该提示问题解决,但程序仍未正常运行
  • 尝试将读取文件的代码移至addButtonClicked()方法或列表显示区域,程序依然崩溃

代码内容

App类

package com.mycompany.zaph_midterm;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class App extends Application {
    public TextField addGrocery;
    public ListView<String> groceryList = new ListView<>();
    @Override
    public void start(Stage stage) {
        stage.setTitle("Grocery List");
        
        //Setting grid
        GridPane grid = new GridPane();
        grid.setAlignment(Pos.CENTER);
        grid.setPadding(new Insets(15,15,15,15));
        grid.setHgap(10);
        grid.setVgap(10);
        
        //attaching grid to scene
        Scene scene = new Scene(grid);
        
        //Setting the add section
        HBox hbox = new HBox();
        GridPane addHbox = new GridPane();
        addHbox.add(new Label ("Items: "), 0, 0);
        addHbox.add(new Label("  "), 1, 0);
        addGrocery = new TextField();
        addHbox.add(addGrocery, 2, 0);
        Button addButton = new Button("Add");
        addButton.setOnAction(event -> addButtonClicked());
        addHbox.add(new Label("  "), 3, 0);
        addHbox.add(addButton, 4, 0);
        hbox.getChildren().add(addHbox);
        grid.add(hbox, 0, 0);
        
        //Setting list to show
        HBox groceryBox = new HBox();
        GridPane groceryGrid = new GridPane();
        groceryGrid.add(new Label("List: "), 0, 0);
        groceryList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        groceryList.getItems().add(grocery.readFromFile(addGrocery.getText())); // 此处grocery未初始化
        groceryGrid.add(groceryList, 2, 0);
        groceryBox.getChildren().add(groceryGrid);
        grid.add(groceryBox, 0, 1);
        
        stage.setScene(scene);
        stage.show();
    }
    // Add button clicked 
    private void addButtonClicked() {
        //Validation of user Input
        Validation v = new Validation();
        String errorMsg = "";
        errorMsg += v.hasValue(addGrocery.getText());
        errorMsg += v.isBlank(addGrocery.getText());
        
        // Using if/else to add to grocery List
        if (errorMsg.isBlank()){
            GroceryList grocery = new GroceryList();
            //Adding to Grocery List
            grocery.printToFile(addGrocery.getText());
            
        }else {
           Alert wrong = new Alert(Alert.AlertType.ERROR);
           wrong.setHeaderText("Error");
           wrong.setContentText(errorMsg);
           wrong.showAndWait();
        }
    }
    
    public static void main(String[] args) {
        launch();
    }
}

Validation类

package com.mycompany.zaph_midterm;

public class Validation {
    private final String lineEnd;
    
   public Validation() {
       this.lineEnd = "\n";
   } 
   
   public Validation(String lineEnd) {
       this.lineEnd = lineEnd;
   }
   
   public String isBlank(String name) {
       String error = "";
       if(name.isBlank()) {
           error = "Must have groceries in the add text box.";
       }
       return error;
   }// end of isBlank
   
   public String hasValue(String name) {
       //if user input is able to be parsed then an error message will return
       String error = "";
       try {
           Double.parseDouble(name);
       } catch (NumberFormatException e) {
           return error;
       }
       error ="Text box must only contain letters";
       return error;
   }
}

GroceryList类

package com.mycompany.zaph_midterm;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import static java.lang.System.out;
import javafx.scene.control.Alert;

public class GroceryList {
    private String grocery;
    
    public GroceryList() {
        grocery = " ";
    }
    
    public void setGroceryList(String args){
        this.grocery = grocery; // 赋值错误,应该是this.grocery = args
    }
    public String getGroceryList() {
        return grocery;
    }
    
    public void printToFile(String grocery) {
        //creating file
        try (PrintWriter out = new PrintWriter(
                               new BufferedWriter(
                               new FileWriter("Grocery.txt")))) { // 默认覆盖模式,未追加
            //writing to file
            out.print(grocery);
            
        }
        //catching error if found
        catch (IOException e){
          System.out.println(e);
        }
    }
    public void deleteFromFile(String grocery) {
        
    }
    public String readFromFile(String grocery) throws IOException{
        try (BufferedReader in = new BufferedReader(
                                 new FileReader("Grocery.txt")))
            // 缺少大括号包裹代码块
            String line = in.readLine();
            while (line != null){
               line = grocery; // 逻辑错误,未读取文件内容
               return grocery;
            }
            
        }
        catch (IOException e) {
            Alert error = new Alert(Alert.AlertType.ERROR); // 不建议在数据类中弹出UI提示
            error.setHeaderText("Didn't work");
            error.setContentText("Something is wrong");
            error.showAndWait();
        }
        return grocery;
    }
}

关键问题分析

  1. GroceryList类语法与逻辑错误:

    • readFromFile方法的try块缺少大括号,导致编译错误
    • readFromFile逻辑错误,未真正读取文件内容,直接返回传入的参数
    • printToFile使用覆盖模式写入文件,每次添加都会清空之前的内容
    • setGroceryList方法赋值错误,未将参数赋值给成员变量
    • 在数据类中弹出Alert,违反职责分离原则,且可能因非FX线程调用崩溃
  2. App类逻辑缺失:

    • 启动时调用grocery.readFromFile,但grocery对象未初始化,直接调用会导致空指针
    • 添加条目到文件后,未刷新ListView,导致界面不更新
    • 错误消息拼接未换行,多个错误会连在一起显示

修复步骤

1. 修复GroceryList类

修改printToFile为追加模式

public void printToFile(String grocery) {
    try (PrintWriter out = new PrintWriter(
                           new BufferedWriter(
                           new FileWriter("Grocery.txt", true)))) { // 添加true开启追加模式
        out.println(grocery); // 用println保证每个条目占一行
    } catch (IOException e){
        System.out.println(e.getMessage());
    }
}

修复readFromFile方法,读取所有条目并返回列表

import java.util.ArrayList;
import java.util.List;

// ...

public List<String> readFromFile() throws IOException {
    List<String> items = new ArrayList<>();
    try (BufferedReader in = new BufferedReader(new FileReader("Grocery.txt"))) {
        String line;
        while ((line = in.readLine()) != null) {
            if (!line.isBlank()) { // 跳过空行
                items.add(line);
            }
        }
    } catch (IOException e) {
        // 抛出异常让上层处理,不要在这弹出Alert
        throw new IOException("读取文件失败: " + e.getMessage(), e);
    }
    return items;
}

修复setGroceryList方法

public void setGroceryList(String args){
    this.grocery = args;
}

2. 修复App类

启动时加载文件内容到ListView

@Override
public void start(Stage stage) {
    // ... 原有界面代码
    
    //Setting list to show
    HBox groceryBox = new HBox();
    GridPane groceryGrid = new GridPane();
    groceryGrid.add(new Label("List: "), 0, 0);
    groceryList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
    
    // 加载文件内容到ListView
    loadGroceryList();
    
    groceryGrid.add(groceryList, 2, 0);
    groceryBox.getChildren().add(groceryGrid);
    grid.add(groceryBox, 0, 1);
    
    stage.setScene(scene);
    stage.show();
}

// 新增加载列表方法
private void loadGroceryList() {
    GroceryList grocery = new GroceryList();
    try {
        groceryList.getItems().clear();
        groceryList.getItems().addAll(grocery.readFromFile());
    } catch (IOException e) {
        Alert error = new Alert(Alert.AlertType.ERROR);
        error.setHeaderText("加载失败");
        error.setContentText(e.getMessage());
        error.showAndWait();
    }
}

修改addButtonClicked方法,添加后刷新ListView

private void addButtonClicked() {
    Validation v = new Validation();
    String errorMsg = "";
    String blankError = v.isBlank(addGrocery.getText());
    String numberError = v.hasValue(addGrocery.getText());
    
    // 拼接错误消息,换行显示
    if (!blankError.isBlank()) {
        errorMsg += blankError;
    }
    if (!numberError.isBlank()) {
        if (!errorMsg.isBlank()) {
            errorMsg += "\n";
        }
        errorMsg += numberError;
    }
    
    if (errorMsg.isBlank()){
        GroceryList grocery = new GroceryList();
        grocery.printToFile(addGrocery.getText());
        addGrocery.clear(); // 清空输入框
        loadGroceryList(); // 刷新列表
    }else {
       Alert wrong = new Alert(Alert.AlertType.ERROR);
       wrong.setHeaderText("输入错误");
       wrong.setContentText(errorMsg);
       wrong.showAndWait();
    }
}

额外优化建议

  • 给Grocery.txt指定完整路径,避免因工作目录问题找不到文件
  • 添加异常处理,处理文件不存在的情况(比如第一次运行时创建空文件)
  • 后续开发删除功能时,注意同步更新文件和ListView

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 05:09:50