Java FX 应用程序中未捕获的异常处理程序将 RuntimeExceptions 捕获为 NullPointerExceptions 的问题

Issue with catching RuntimeExceptions as NullPointerExceptions by an uncaught exception handler in Java FX applications

我读了这个postJavaFx 8 global exception handling and tried to handle uncaught exceptions in my application. It works fine as described in the post. But when I added a statement which caused a NullPointerException the UncaughtExceptionHandler did not catch this exception. Why ? Is there another thread handling this exception? Or do I have to set the DefaultUncaughtExceptionHandler? I read JavaDocs:

Uncaught exception handling is controlled first by the thread, then by the thread's ThreadGroup object and finally by the default uncaught exception handler. If the thread does not have an explicit uncaught exception handler set, and the thread's thread group (including parent thread groups) does not specialize its uncaughtException method, then the default handler's uncaughtException method will be invoked.

我不知道如何获得处理所有未捕获异常的解决方案。你能帮我吗?感谢您的支持!!

这是代码:

package TestSimpleDialog;

public class Main extends Application {
    private final Logger logger = Logger.getLogger(this.getClass().getName());
    private MyHandler myHandler = new MyHandler();
    @Override
    public void init() {
    // Thread.currentThread is the FX-Launcher thread:
    Thread.currentThread().setUncaughtExceptionHandler(myHandler);
    System.out.println(Thread.currentThread().getUncaughtExceptionHandler());
    try {
        logger.addHandler(new FileHandler("java.myLOG"));
    }
    catch (IOException e) {
        throw new IllegalStateException("IOException when adding File Handler");
    }
    }
    @Override
    public void start(Stage primaryStage) {
    logger.info("Test Application started");
    // Thread.currentThread() is the FX-Application thread:
    Thread.currentThread().setUncaughtExceptionHandler(myHandler);
    // If this thread has not had an uncaught exception handler explicitly set then this thread's ThreadGroup object
    // is returned, unless this thread has terminated, in which case null is returned.
    System.out.println(Thread.currentThread().getUncaughtExceptionHandler());

    //        try {
    //            URI uriTest = new URI(null);
    //        } catch (URISyntaxException e) {
    //            throw new IllegalStateException("URISyntaxException by testing");
    //        }

    StackPane root = new StackPane();
    Button button = new Button("Throw exception");
    button.setOnAction(event -> {
        throw new RuntimeException("** T E S T **") ;
    });
    root.getChildren().add(button);
    Scene scene = new Scene(root, 150, 60);
    primaryStage.setScene(scene);
    primaryStage.show();
    }

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

    class MyHandler implements Thread.UncaughtExceptionHandler{
    @Override
    public void uncaughtException(Thread thread, Throwable throwable) {
        System.out.println("MyHandler caught exception: "+throwable.getMessage());
        logger.log(Level.SEVERE, "**TEST** threw an uncaught exception", throwable);
    }
    }
}

当我按下按钮时,我在控制台上得到了这个输出:

TestSimpleDialog.Main$MyHandler@49285759
Aug. 08, 2020 5:55:33 NACHM. TestSimpleDialog.Main start
INFORMATION: Test Application started
TestSimpleDialog.Main$MyHandler@49285759
MyHandler caught exception: ** T E S T **
Aug. 08, 2020 5:55:51 NACHM. TestSimpleDialog.Main$MyHandler uncaughtException
SCHWERWIEGEND: **TEST** threw an uncaught exception
java.lang.RuntimeException: ** T E S T **
    at TestSimpleDialog.Main.lambda$start[=12=](Main.java:47)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher............

但是当我激活这个语句得到一个NullPointerException

try {
    URI uriTest = new URI(null);
} catch (URISyntaxException e) {
    throw new IllegalStateException("URISyntaxException by testing");
}

我可以在控制台上看到异常未被捕获,因为 class MyHandler 在 Sysout 上打印缺少语句“MyHandler 捕获异常:”。此外,日志文件中没有任何内容。

TestSimpleDialog.Main$MyHandler@22b2aa29
TestSimpleDialog.Main$MyHandler@22b2aa29
Aug. 08, 2020 6:16:51 NACHM. TestSimpleDialog.Main start
INFORMATION: Test Application started
Exception in Application start method
java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:464)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: Exception in Application start method
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication(LauncherImpl.java:195)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.NullPointerException
    at java.base/java.net.URI$Parser.parse(URI.java:3104)
    at java.base/java.net.URI.<init>(URI.java:600)
    at TestSimpleDialog.Main.start(Main.java:41)
    at javafx.graphics/com.sun.javafx.application.............

没有 how 的答案 - 只是对 why 的初步解释(看起来像是我评论中的第一个想法不远了 ;)

其基础是应用程序是通过反射实例化的事实:init/start 中发生的任何异常都会作为实例化错误冒出来,即 InvocationTargetException。而这些确实是由 LauncherImpl.launchApplicationWithArgs by .. ex.printStackTrace

处理的
public static void launchApplicationWithArgs(final ModuleAccess mainModule,
        final String mainClassName,
        final String preloaderClassName, String[] args) {

  // invoke, handle exception, line 472
  ...
    } catch (InvocationTargetException ex) {
        ex.printStackTrace();
        abort(null, "Exception running application %1$s", tempAppClass.getName());
        return;
    }

看不到任何拦截方法(这可能是一个错误..或不是)。

编辑

要实现合并到 InvocationTargetException 中的错误的日志记录(除了打印到错误输出之外),一个选项可能是将 init/start 方法的工作负载包装到 try .. catch 中。 .. 块并手动调用处理程序,例如

@Override
public void init() throws Exception {
    try {
        // do stuff that might be throwing
        throw new ArithmeticException("am I caught?");
    } catch (Exception ex) {
        // invoke the handler and re-throw
        myHandler.uncaughtException(Thread.currentThread(), ex);
        throw(ex);
    }

}