提问人:Zapherail 提问时间:10/21/2023 最后编辑:Zapherail 更新时间:10/22/2023 访问量:97
尝试从文件读取时 JavaFX 构建崩溃
JavaFX build crashes when trying to read from file
问:
我正在做一个期中考试,我们必须制作一个 GUI 购物清单。教授要求我们将列表制作成一个.txt文件,并从该文件中读取。还有一个删除部分,删除所有我尚未编码的内容。通常,每当我在代码中添加新内容时,我都会测试代码。在尝试测试这个时,我注意到它崩溃了。代码正在写入Grocery.txt,但未显示写入列表框的内容。我知道我很可能错过了一些小东西。当我看我的教科书“Murach 的 Java 编程第 6 版”时,它看起来几乎相同,只是他们使用的示例是从文件中读取三个不同的东西。他们使用数组进行读取。我试图将每个放在自己的行上。我知道现在不会。我正在尝试测试它是否读取它,而目前它还没有。
更新:我现在更改了一些代码,当我单击添加时,我收到一个空的警报框,上面写着错误。放置更新的代码。我会呆几个小时,然后会回来工作。谢谢你的帮助。
最终更新:由于某种原因,在我的错误中发现了错误,它没有正确读取它,将其更改为修复它。感谢大家的意见,每当我需要再次发布时,我都会更好地发布问题。该程序仍然无法正常运行,但我遇到的问题已得到解决,其余的我可以自己解决。if (errorMsg == " ") {
if(errorMsg.isBlank())
再次感谢您和愉快的编程
这是我目前的代码:
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()));
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();
}
验证类:
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;
public class GroceryList {
private String grocery;
public GroceryList() {
grocery = " ";
}
public void setGroceryList(String args){
this.grocery = grocery;
}
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);
error.setHeaderText("Didn't work");
error.setContentText("Something is wrong");
error.showAndWait();
}
return grocery;
}
}
}
我尝试将 try(BufferedReader) 移动到 addButtonCLicked() 仍然崩溃,所以我将其移到 //设置列表下以显示,但它仍然崩溃。
答:
您的问题没有重点且不清楚。但是,为了好玩,我将尝试修改您的代码。
在关注点分离中编程中,这是一个非常有用的概念。这意味着将代码组织在单独的块中,每个块都专注于特定的作业。
一项工作是将您的购物清单项目写入存储,然后将它们读回来。这应该在一个特定的类中。我们将这个类称为 。Repository
我们需要定义要进入存储库的对象。你的班级构思不周,因为列表只是项目的集合。这是我们需要定义的项目。我们可以通过记录来做到这一点。此类的唯一工作是表示表示来自现实世界的杂货项目所需的已验证状态。GroceryList
public record GroceryItem( String name )
{
public GroceryItem ( final String name )
{
Objects.requireNonNull ( name );
if ( name.isBlank ( ) ) throw new IllegalArgumentException ( "Grocery item name cannot be blank." );
this.name = name;
}
}
存储库需要保存杂货项目并再次检索。这个类的唯一工作是持久存储和检索对象的状态。在这里,我们编写了一个简单的虚假实现,足以开始。
public class Repository
{
private List < GroceryItem > storedGroceryItems =
new ArrayList <> (
List.of (
new GroceryItem ( "Bananas" ) ,
new GroceryItem ( "Eggs" ) ,
new GroceryItem ( "Olive oil" )
)
);
public List < GroceryItem > fetchGroceryItems ( )
{
return List.copyOf ( this.storedGroceryItems );
}
public boolean saveGroceryItem ( final GroceryItem groceryItem )
{
if ( this.storedGroceryItems.contains ( groceryItem ) ) return false;
return this.storedGroceryItems.add ( groceryItem );
}
public boolean deleteGroceryItem ( final GroceryItem groceryItem )
{
return this.storedGroceryItems.remove ( groceryItem );
}
public boolean removeAllGroceryItems ( )
{
List < GroceryItem > emptyList = List.of ( );
return this.storedGroceryItems.retainAll ( emptyList );
}
}
编写一些测试进行验证。我们期待所有“真实”的结果。
public class TestRepository
{
public static void main ( String[] args )
{
TestRepository.testPreloadedRepository ( );
}
public static void testPreloadedRepository ( )
{
Repository repository = new Repository ( );
boolean success;
List < GroceryItem > list = repository.fetchGroceryItems ( );
System.out.println ( "list has 3 elements: " + ( list.size ( ) == 3 ) );
List < GroceryItem > expectedList =
List.of (
new GroceryItem ( "Bananas" ) ,
new GroceryItem ( "Eggs" ) ,
new GroceryItem ( "Olive oil" )
);
System.out.println ( "list has expected items: " + ( list.equals ( expectedList ) ) );
GroceryItem broccoli = new GroceryItem ( "Broccoli" );
success = repository.saveGroceryItem ( broccoli );
System.out.println ( "success = " + success );
list = repository.fetchGroceryItems ( );
System.out.println ( "list has 4 elements: " + ( list.size ( ) == 4 ) );
expectedList =
List.of (
new GroceryItem ( "Bananas" ) ,
new GroceryItem ( "Eggs" ) ,
new GroceryItem ( "Olive oil" ) ,
broccoli
);
System.out.println ( "list has expected items: " + ( list.equals ( expectedList ) ) );
GroceryItem eggs = new GroceryItem ( "Eggs" );
success = repository.deleteGroceryItem ( eggs );
System.out.println ( "success = " + success );
list = repository.fetchGroceryItems ( );
System.out.println ( "list has 3 elements: " + ( list.size ( ) == 3 ) );
expectedList =
List.of (
new GroceryItem ( "Bananas" ) ,
new GroceryItem ( "Olive oil" ) ,
broccoli
);
System.out.println ( "list has expected items: " + ( list.equals ( expectedList ) ) );
}
}
我们所有的测试都通过了。
list has 3 elements: true
list has expected items: true
success = true
list has 4 elements: true
list has expected items: true
success = true
list has 3 elements: true
list has expected items: true
给高级学生的提示:在实际工作中,我们会使用像 Jupiter 这样的框架来编写测试。然后,我们在 JUnit 5 等测试工具中运行测试。
现在在 JavaFX 中构建 GUI 实现为 OpenJFX。
public class HelloApplication extends Application
{
private Repository repository = new Repository ( );
@Override
public void start ( Stage stage ) throws IOException
{
Pane pane = this.buildPane ();
Scene scene = new Scene ( pane , 500 , 350 );
stage.setTitle ( "Grocery List" );
stage.setScene ( scene );
stage.show ( );
}
private Pane buildPane () {
// Data
List < GroceryItem > groceryItems = this.repository.fetchGroceryItems ( );
ObservableList < GroceryItem > observableList = FXCollections.observableArrayList ( groceryItems );
// Widgets
ListView < GroceryItem > listView = new ListView <> ( observableList );
TextField newGroceryItemField = new TextField ( );
newGroceryItemField.setPromptText ( "new grocery item" );
Button addButton = new Button ( "Add…" );
Button removeButton = new Button ( "Delete" );
// Behavior
addButton.setOnMouseClicked ( ( MouseEvent mouseEvent ) ->
{
String input = newGroceryItemField.getText ( );
if ( input.isBlank ( ) ) return;
GroceryItem groceryItem = new GroceryItem ( input );
if ( observableList.contains ( groceryItem ) ) return;
if ( this.repository.saveGroceryItem ( groceryItem ) )
{
observableList.add ( groceryItem );
}
} );
removeButton.setOnMouseClicked ( ( MouseEvent mouseEvent ) ->
{
GroceryItem groceryItem = listView.getSelectionModel ( ).getSelectedItem ( );
if ( Objects.nonNull ( groceryItem ) )
{
if ( this.repository.deleteGroceryItem ( groceryItem ) )
{
observableList.remove ( groceryItem );
}
}
} );
// Arrange
BorderPane borderPane = new BorderPane ( );
borderPane.setCenter ( listView );
borderPane.setBottom ( new HBox ( newGroceryItemField , addButton , removeButton ) );
return borderPane;
}
public static void main ( String[] args ) { launch ( ); }
}
一切正常后,我们可以回到我们的.我们可以重写以实际保存到文件中,而不是使用内存中的虚拟列表。Repository
查看 Java NIO 类(如 Path
、Paths
和 Files
)以简化文件处理。
package work.basil.example.exgrocerylist;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
public class Repository
{
private Path dataFilePath = Paths.get ( "/Users/basil_dot_work/GroceryItems.txt" );
public List < GroceryItem > fetchGroceryItems ( )
{
boolean go = this.createAndPopulateFileIfNotExists ( );
try
{
List < String > lines = Files.readAllLines ( this.dataFilePath , StandardCharsets.UTF_8 );
List < GroceryItem > groceryItems = new ArrayList <> ( );
for ( String line : lines )
{
if ( line.isBlank ( ) )
{
continue;
}
GroceryItem groceryItem = new GroceryItem ( line );
groceryItems.add ( groceryItem );
}
return List.copyOf ( groceryItems );
}
catch ( IOException e )
{
throw new RuntimeException ( e );
}
}
private boolean createAndPopulateFileIfNotExists ( )
{
if ( Files.exists ( this.dataFilePath ) ) return true;
List < GroceryItem > defaultGroceryItems =
List.of (
new GroceryItem ( "Bananas" ) ,
new GroceryItem ( "Eggs" ) ,
new GroceryItem ( "Olive oil" )
);
List < String > lines =
defaultGroceryItems.stream ( ).map ( GroceryItem :: name ).toList ( );
try
{
Files.write ( this.dataFilePath , lines , StandardCharsets.UTF_8 );
return ( this.fetchGroceryItems ( ).equals ( defaultGroceryItems ) );
}
catch ( IOException e )
{
throw new RuntimeException ( e );
}
}
public boolean saveGroceryItem ( final GroceryItem groceryItem )
{
List < GroceryItem > groceryItems = this.fetchGroceryItems ( );
if ( groceryItems.contains ( groceryItem ) ) return false;
try
{
Files.write ( this.dataFilePath , groceryItem.name ( ).getBytes ( ) , StandardOpenOption.APPEND );
}
catch ( IOException e )
{
throw new RuntimeException ( e );
}
return this.fetchGroceryItems ( ).contains ( groceryItem );
}
public boolean deleteGroceryItem ( final GroceryItem groceryItem )
{
List < GroceryItem > groceryItems = this.fetchGroceryItems ( );
if ( ! groceryItems.contains ( groceryItem ) ) return false;
List < GroceryItem > groceryItemsModifiable = new ArrayList <> ( groceryItems );
boolean go = groceryItemsModifiable.remove ( groceryItem );
if ( ! go )
{
return false;
}
List < String > lines = groceryItemsModifiable.stream ( ).map ( GroceryItem :: name ).toList ( );
try
{
Files.write ( this.dataFilePath , lines , StandardCharsets.UTF_8 );
return true;
}
catch ( IOException e )
{
throw new RuntimeException ( e );
}
}
public boolean removeAllGroceryItems ( )
{
List < String > lines = List.of ( );
try
{
Files.write ( this.dataFilePath , lines , StandardCharsets.UTF_8 );
return ( this.fetchGroceryItems ( ).isEmpty ( ) );
}
catch ( IOException e )
{
throw new RuntimeException ( e );
}
}
}
给高级学生的提示:应该是一个接口,有两个具体的实现:一个用于我们的内存列表,另一个用于我们的文件存储。Repository
小心。。。此基于文件有一个错误:用户无法添加多个杂货项目。我们可以确定 bug 存在于这个基于文件的实现中,因为我们可以切换回基于内存列表的实现来查看 bug 消失。这种错误验证对于编写更简单的虚假实现来说是一个很大的好处。我会把未发现的错误留在原地,因为我已经为学校作业做了太多。Repository
Repository
另一个问题:尽管这个基于文件的实现中存在错误,但我们所有的测试都通过了。这意味着我们的测试是不够的,需要修改。我也会把它留给读者做一个练习。
评论
readFromFile
void
String
Grocery.txt
line = grocery;
if-statement
readFromFile
grocery
line = grocery;
printToFile
readFromFile
Grocery.txt