我是 JavaFX 的新手,正在尝试使用按钮 运行 文本分析器

I'm new to JavaFX, trying to use buttons to run a text analyzer

在我尝试使用 HBox 之前一切都很好。现在我收到一个很长的“应用程序构造函数中的异常”错误。

java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:465)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:364)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1071)
Caused by: java.lang.RuntimeException: Unable to construct Application instance: class Text_Processor
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:891)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication(LauncherImpl.java:196)
    at java.base/java.lang.Thread.run(Thread.java:831)
Caused by: java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:78)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1(LauncherImpl.java:803)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait(PlatformImpl.java:484)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:457)
    at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater(PlatformImpl.java:456)
    at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop(WinApplication.java:184)
    ... 1 more
Caused by: java.lang.NullPointerException: Children: child node is null: parent = HBox@4f9175a0
    at javafx.graphics/javafx.scene.Parent.onProposedChange(Parent.java:542)
    at javafx.base/com.sun.javafx.collections.VetoableListDecorator.addAll(VetoableListDecorator.java:233)
    at javafx.base/com.sun.javafx.collections.VetoableListDecorator.addAll(VetoableListDecorator.java:103)
    at javafx.graphics/javafx.scene.layout.HBox.<init>(HBox.java:251)
    at Text_Processor.<init>(Text_Processor.java:24)
    ... 14 more

例外运行申请Text_Processor

import java.io.*;
import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;
import java.net.URL;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.util.Collections;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class Text_Processor extends Application implements EventHandler<ActionEvent> {
    Button button, button2, button3;
    HBox hbox = new HBox(button, button2, button3);

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setTitle("Word Analyzer");

        button = new Button();
        button.setText("Click First to Fetch The Raven Text");
        button.setOnAction(this);
        button2 = new Button();
        button2.setText("Click Here Next to parse the file");
        button2.setOnAction(this);
        button3 = new Button();
        button3.setText("Click Here Last to display results");
        button3.setOnAction(this);
        hbox.getChildren().addAll(button, button2, button3);

        Scene scene = new Scene(hbox, 200, 100);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    @Override
    public void handle(ActionEvent event) {
        if (event.getSource() == button) ;
        {
            System.out.println("Fetching Text From Source https://www.gutenberg.org/files/1065/1065-h/1065-h.htm");
            URL url;
            try {
                url = new URL("https://www.gutenberg.org/files/1065/1065-h/1065-h.htm"); //Create object for URL.
                BufferedReader readr = new BufferedReader(new InputStreamReader(url.openStream())); //Open a reader to stream the file
                BufferedWriter writer = new BufferedWriter(new FileWriter("Test_Download.txt")); //Create file to store the downloaded info.
                String line;
                while ((line = readr.readLine()) != null) //Read the html source code from the website.
                {
                    writer.write(line); //Write the html code to a local file.
                }
                readr.close();
                writer.close();
            } catch
            (IOException e) {
                System.out.println("Error: " + e.getMessage());
                e.printStackTrace();
            }

        }

        if (event.getSource() == button2) ;
        {
            System.out.println("Parsing text of your downloaded file...");
            try {
                File input = new File("Test_Download.txt"); //Open the newly written file containing the HTML from The Raven.
                Document doc = Jsoup.parse(input, "UTF-8"); //Parse the file to eliminate the tags
                String body = doc.body().text(); //Save the parsed words to a string.
                BufferedWriter writer = new BufferedWriter(new FileWriter("Test_Download.txt"));//rewrite the file without the tags.
                writer.write(body);
                writer.close();
            } catch (IOException e) {
                System.out.println("Error: " + e.getMessage());
                e.printStackTrace();
            }

        }

        if (event.getSource() == button3) ;
        {
            try {
                File file = new File("Test_Download.txt"); //Open the file we have written for a word count analysis.
                Scanner sc = new Scanner(file); //Create a scanner to read the rewritten text file.
                int i = 0, indexOfWord = 0, count = 0;
                List<String> words = new ArrayList<String>(); //Create two arrays to store the words and word count.
                List<Integer> wordCount = new ArrayList<Integer>();

                while (sc.hasNext()) //Starting here we read each individual word of the file.
                {
                    String word = sc.next();
                    word = word.replaceAll("\p{Punct}", " "); //This removes the punctuation and any non letters.;
                    if (words.contains(word))//Here if the word already exist we just increment a counter for that word.
                    {
                        indexOfWord = words.indexOf(word);
                        count = wordCount.get(indexOfWord);
                        count = count + 1;
                        wordCount.add(indexOfWord, count);
                    } else {
                        words.add(i, word);
                        wordCount.add(i, 1);
                        i++;
                    }
                }
                sc.close();


                Integer max = Collections.max(wordCount); //At this point we go through the array and sort it by max occurrences of words
                System.out.println("max word occurence is:" + max);// and then display them in descending order.
                System.out.println("|Count|Word");
                System.out.println("|-----|-----------------|");

                int no_of_elements = words.size();

                while (max != 0) {
                    for (int j = 0; j < no_of_elements; j++) {
                        String word = words.get(j);
                        count = wordCount.get(j);
                        if (count == max) {
                            //if(word.isEmpty()){continue;}// I put this in because the program was displaying random blanks for some reason.
                            System.out.printf("|%-4d", count);
                            System.out.printf(" |" + word + "\n");
                        }

                    }
                    max--;
                }
            } catch (IOException e) {
                System.out.println("Error: " + e.getMessage());
                e.printStackTrace();
            }

        }
    }
}

错误不言自明。

Caused by: java.lang.NullPointerException: Children: child node is null: parent = HBox@4f9175a0
  . . .
  at Text_Processor.<init>(Text_Processor.java:24)

这是第二行造成的:

Button button, button2, button3;
HBox hbox = new HBox(button, button2, button3);

显然,按钮引用在传递给 HBox 构造函数时都是空的,这是不允许的。

要修复它,只需将构造函数调用更改为:

HBox hbox = new HBox();

无论如何您稍后都会在应用程序中将按钮添加到 hbox,因此在这种情况下您不需要在初始化时执行此操作。稍后在你的代码中你已经有了这个:

hbox.getChildren().addAll(button, button2, button3);

与你的问题正交,但在风格注释上(如果你愿意,你可以忽略它),我不会在应用程序上实现 EventHandler class。我认为应用程序 class 最好定义为仅实现应用程序生命周期。

相反,我会通过按钮的操作来命名我的按钮,并提供具有相同名称的方法,例如:

Button fetchButton = new Button("Click First to Fetch The Raven Text"); 
fetchButton.setOnAction(e -> fetch());

. . .

private void fetch() {
    // perform the fetch task.
}

理想情况下,fetch 应该在单独的服务中定义 class,这样它就可以独立于 UI 进行单元测试,但是将它作为一个方法放在应用程序中对于小型应用程序来说会很好申请。

其他任务也类似。

然后你可以摆脱 (IMO) 更难阅读、更容易出错和更难测试的通用操作事件处理程序(我认为它有错误,因为你在 ; 之后放置if 条件和 { 代码块开始之前)。

另外,在风格说明上,不要使用 Text_Processor、google Java 等命名约定并应用它们。它使您的代码对其他 Java 开发人员而言更具可读性和更容易理解。另外一些工具假定遵循命名约定,如果不遵循则中断(例如 JavaFX PropertyValueFactory)。