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; } }
关键问题分析
GroceryList类语法与逻辑错误:
readFromFile方法的try块缺少大括号,导致编译错误readFromFile逻辑错误,未真正读取文件内容,直接返回传入的参数printToFile使用覆盖模式写入文件,每次添加都会清空之前的内容setGroceryList方法赋值错误,未将参数赋值给成员变量- 在数据类中弹出Alert,违反职责分离原则,且可能因非FX线程调用崩溃
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
相关产品推荐
相关产品推荐

