JavaFX应用程序抛出NullPointerExceptions但仍然运行

Lor*_*aad 9 java javafx nullpointerexception

我运行的每个JavaFX应用程序都抛出两个NullPointerExceptions.它们不会阻止甚至影响项目的执行,只有在调试模式下运行应用程序时才能看到它们.我甚至遇到了来自Oracle的HelloWorld示例和这个最小程序的问题:

public class JavaFXTSample extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {

        StackPane iAmRoot = new StackPane();

        Scene scene = new Scene(iAmRoot, 300, 250);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main (String[] args) {
        launch(args);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是第一个错误的堆栈跟踪:

 Thread [main] (Suspended (exception NullPointerException)) 
    SystemProperties.setVersions() line: 81 [local variables unavailable]   
    SystemProperties.lambda$static$28() line: 67    
    30621981.run() line: not available  
    AccessController.doPrivileged(PrivilegedAction<T>) line: not available [native method]  
    SystemProperties.<clinit>() line: 64    
    LauncherImpl.startToolkit() line: 668   
    LauncherImpl.launchApplicationWithArgs(String, String, String[]) line: 337  
    LauncherImpl.launchApplication(String, String, String[]) line: 328  
    NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) line: not available [native method]  
    NativeMethodAccessorImpl.invoke(Object, Object[]) line: not available   
    DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: not available   
    Method.invoke(Object, Object...) line: not available    
    LauncherHelper$FXHelper.main(String...) line: not available 
Run Code Online (Sandbox Code Playgroud)

这是第二个:

Thread [JavaFX Application Thread] (Suspended (exception NullPointerException)) 
    PropertyHelper.lambda$getBooleanProperty$514(String) line: 39   
    7164036.run() line: not available   
    AccessController.doPrivileged(PrivilegedAction<T>) line: not available [native method]  
    PropertyHelper.getBooleanProperty(String) line: 37  
    Parent.<clinit>() line: 87  
    JavaFXTSample.start(Stage) line: 16 
    LauncherImpl.lambda$launchApplication1$162(AtomicBoolean, Application) line: 863    
    2266602.run() line: not available   
    PlatformImpl.lambda$runAndWait$175(Runnable, CountDownLatch) line: 326  
    32251660.run() line: not available  
    PlatformImpl.lambda$null$173(Runnable) line: 295    
    11305869.run() line: not available  
    AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]    
    PlatformImpl.lambda$runLater$174(Runnable, AccessControlContext) line: 294  
    30052382.run() line: not available  
    InvokeLaterDispatcher$Future.run() line: 95 
    WinApplication._runLoop(Runnable) line: not available [native method]   
    WinApplication.lambda$null$148(int, Runnable) line: 191 
    32126786.run() line: not available  
    Thread.run() line: not available    
Run Code Online (Sandbox Code Playgroud)

更重要的是,如果我删除任何实例iAmRootscene(所以start()只是读取primaryStage.show();),则不会发生第二个错误.为什么会这样?

我以前能够找到这个问题(JavaFX应用程序在启动时抛出NullPointerException),但似乎没有人解决它,并且它在2年前被问到.

如果它有帮助,我在Windows 7 Professional上运行Eclipse 4.5.2,我认为我根本不使用FXML.

编辑:

为了它的价值,我找不到第二个错误的源代码,但我找到了抛出第一个错误的方法的JavaFX代码(第81行):

58  private static final String versionResourceName =
59       "/com/sun/javafx/runtime/resources/version.properties";

...

78 private static void setVersions() {
79     int size;
80     InputStream is =
81         SystemProperties.class.getResourceAsStream(versionResourceName);
82     try  {
83         size = is.available();
84         
85         byte[] b = new byte[size];
86         int n = is.read(b);            
87         String inStr = new String(b, "utf-8");
88         SystemProperties.setFXProperty("javafx.version",
89             getValue(inStr, "release="));
90 
91         SystemProperties.setFXProperty("javafx.runtime.version",
92             getValue(inStr, "full="));
93 
94      } catch (Exception ignore) {
95      }
96 }
Run Code Online (Sandbox Code Playgroud)

Tho*_*uck 8

放弃

这个问题是1岁,但我觉得仍然非常相关.这就是为什么我回答这个问题(详细说明).

我会尽力回答这个问题

TL; DR

JavaFX有一些古怪的代码行,这些异常是由它们引起的.所有这些都可以追溯到JavaFX中的某些行,它会错过检查或使用未经检查的部分.

我运行的每个JavaFX应用程序都抛出两个NullPointerExceptions.它们不会阻止甚至影响项目的执行,只有在调试模式下运行应用程序时才能看到它们.

第一个NullPointerException

您显示的第一个例外位于:

SystemProperties.setVersions()行:81 [局部变量不可用]

您已经发布了相关代码.为了形象化,我将再次发布.问题的关键是:

InputStream is =
            SystemProperties.class.getResourceAsStream(versionResourceName);
try  {
    size = is.available(); // InputStream "is" = null
    ...
} catch (Exception ignore) {
}
Run Code Online (Sandbox Code Playgroud)

这很容易解释.该getResourceAsStream方法返回以下内容(来自JavaDoc):

返回:InputStream对象;如果未找到具有此名称的资源,则返回null

这意味着"/com/sun/javafx/runtime/resources/version.properties"找不到资源(您再次发布的资源).

然而,这不会被处理,而是被忽略.这个例外是因为没有打印,但仍然被抛出.它是由调试器捕获的,但就是这样.因此只能由调试器识别.

为什么不能很好地识别.一种可能性是JDK不包含资源.在我对JDK-10(我目前使用的)的测试中,包com.sun.javafx.runtime存在,但子包资源不存在.这不是真正可重现的,因为com.sun包似乎没有记录.这里记录了javafx的基本api ,但com.sun.javafx包不是.如果您使用的是复杂的IDE(例如IntelliJ),您可以查看它.我的JDK-8确实包含它.我在Ubuntu 18.04.1上使用Oracle JDK.

一种可能性是,包裹已经被丢弃了.也许有人认为,通过包引用资源并不是那么好并将其放在资源路径中,但由于异常被吞没到什么都没有,因此从未检测到此错误.

解决了这个问题

注意:此潜在修复未经过彻底测试

如果手动引入资源,则可能会修复此问题.考虑到您发布的代码(并且可以在SystemProperties.setVersions方法中找到),您必须在com.sun.javafx.runtime.resources包中创建一个名为"version.properties"的文件.这个versions.properties应该是这样的:

release=1
full=1
Run Code Online (Sandbox Code Playgroud)

那些是您必须测试的任意值.

第二个NullPointerException

第二个NullPointer根植于行内PropertyHelper.lambda$getBooleanProperty$514(String) line: 39.此方法如下所示:

static boolean getBooleanProperty(final String propName) {
    try {
        boolean answer = AccessController.doPrivileged((java.security.PrivilegedAction<Boolean>) () -> {
                    String propVal = System.getProperty(propName);
                    return "true".equals(propVal.toLowerCase()); // Line 39
                });
        return answer;
    } catch (Exception any) {
    }
    return false;
}
Run Code Online (Sandbox Code Playgroud)

这导致了结论,即propValnull,它由System.getProperty中JavaDoc支持

返回:系统属性的字符串值,如果没有具有该键的属性,则返回null.

这意味着,propName找不到.但是propName方法论证.那么,是什么propName?这也可以在堆栈跟踪中找到.这条线:

父.()行:87

定义应该找到的属性.它读取以下内容:

private static final boolean warnOnAutoMove = PropertyHelper.getBooleanProperty("javafx.sg.warn");
Run Code Online (Sandbox Code Playgroud)

因此,之前说明的方法试图找到SystemProperty"javafx.sg.warn",但不检查它是否存在.这再次没有被处理,因此只在调试器中可见.

如果"toLowerCase"调用将被删除,则不会发生此异常.

修复此例外

您可以通过在以任何方式引用Parent类之前引入以下代码来简单地修复此异常:

System.setProperty("javafx.sg.warn", "true");
Run Code Online (Sandbox Code Playgroud)

它必须在任何引用之前完成,因为此属性是静态的.因此,如果在代码中以任何方式引用此类,则会加载它.一种可能性是,向主类添加一个包含此代码的静态块.另一种方法是将以下内容添加到命令行:

-Djavafx.sg.warn = "真"

这将消除在引用之前调用代码的需要.当然,你可以换取其他任何东西.但有了这个,属性存在,问题行不会抛出NullPointerException.

为什么第二个Exception如果只Stage.show执行则不会被抛出?

这很简单.在JavaFx中,要显示的任何元素都Node场景图中.每个节点都可以有一个Node Parent,它是Node的子类.几乎所有的类都扩展了Parent,StackPane也是如此,但不是Stage.通过引用StackPane,您可以引用Parent,它会触发属性的加载.如果未设置StackPane,则不引用Parent类,这意味着您不会触发加载属性.所以不会抛出异常.

你应该修复例外吗?

由于javafx背后的团队似乎忽略了这些异常,你也可以.我不是说这是一种好的做法,或者您应该以这种方式编写代码,但是修复那些不会阻止您运行此应用程序并且不会改变行为的问题可能会花费更多的时间而不是它的价值.

进一步的想法

这个答案解释了原始的"为什么",但我想把我的两分钱给真正的问题.请注意,这只是我的意见!如果你的想法不同,那就完全可以了.不再直接需要回答这个问题.

所有这一切的根源(在我看来)javafx api中有许多代码味道.从所有部分中的魔术字符串开始,在JavaFX组件内的所有ParentHelper.ParentAccessor实现中向下转换,传递许多大类(甚至是上帝类)并最终以不恰当的亲密关系结束.抽象本来可以更好,并且(例如)Node类的大小是巨大的.

随着新的发布周期,我的希望很高,他们花时间消除至少一些代码味道,但我担心他们只是建立在那些代码气味,这意味着在某个时候,一个新的("现代") )必须构建GUI-API.

最后

祝你今天愉快!