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

时间:2017-06-21 19:21:10

标签: 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);
    }
}

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

 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 

这是第二个:

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    

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

我之前能够找到这个问题(JavaFX application throws NullPointerException at startup),但似乎没有人解决它,并且在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 }

1 个答案:

答案 0 :(得分: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) {
}

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

  

返回:一个InputStream对象;如果找不到具有该名称的资源,则为null

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

这是处理的,只是被忽略了。因此未打印此异常,但仍抛出该异常。它被调试器捕获,仅此而已。因此只能由调试器识别。

原因尚不清楚。一种可能是JDK不包含资源。在我对JDK-10的测试中(我目前正在使用),包com.sun.javafx.runtime存在,但子包资源不存在。由于com.sun软件包似乎没有记录,因此这实际上不是可重复的。 here记录了Javafx的基本api,但没有com.sun.javafx软件包。如果您使用的是复杂的IDE(例如IntelliJ),则可以查看一下。我的JDK-8确实包含它。我在Ubuntu 18.04.1。上使用Oracle JDK。

一种可能是包装已沿途抛弃。也许有人认为,通过包引用资源不是那么好,并将其放在资源路径中,但是由于该异常被吞没了,因此从未检测到该错误。

解决了这个问题

注意:此潜在修补程序尚未经过全面测试

如果您手动引入该资源,则可能会解决此问题。考虑到您发布的代码(可以在SystemProperties.setVersions方法中找到),您必须在com.sun.javafx.runtime.resources包中创建一个名为“ version.properties”的文件。此版本。属性应如下所示:

release=1
full=1

这些是必须测试的任意值。

第二个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;
}

这得出结论,propVal为空,并得到JavaDoc from System.getProperty的支持

  

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

这意味着找不到propName。但是propName是方法参数。那么,propName是什么?再次可以在堆栈跟踪中找到。该行:

  

Parent。()第87行:

定义应找到的属性。它显示以下内容:

private static final boolean warnOnAutoMove = PropertyHelper.getBooleanProperty("javafx.sg.warn");

因此,前面所述的方法尝试找到SystemProperty“ javafx.sg.warn”,但不检查它是否存在。再次无法处理,因此仅在调试器中可见。

如果删除“ toLowerCase”调用,则不会发生此异常。

修复此异常

您可以通过以下任何一种方式简单地解决此异常,只需引入以下代码即可之前以任何方式引用Parent类:

System.setProperty("javafx.sg.warn", "true");

它必须在任何引用之前完成,因为此属性是静态的。因此,如果以任何方式在代码中引用了此类,则将加载它。一种可能是在您的主类中添加一个静态块,其中包含此代码。另一种方法是添加以下to the command line

  

-Djavafx.sg.warn =“ true”

这将消除在引用之前调用代码的需要。当然,您可以将True换成其他任何东西。但是,有了该属性,问题行将不会抛出NullPointerException。

如果仅执行Stage.show,为什么不引发第二个异常?

这很简单。在JavaFx中,要显示的任何元素都是Scene graph中的Node。每个节点可能有一个Parentwhich is a subclass of Node。几乎所有类都扩展了父级so does the StackPane,但扩展了not the Stage。通过引用StackPane,您引用了Parent,这触发了属性的加载。如果未设置StackPane,则不会引用Parent类,这意味着您不会触发属性的加载。因此不会引发异常。

您应该解决例外情况吗?

由于javafx背后的团队似乎忽略了这些异常,因此您也可以这样做。我并不是说这是一种好习惯,也不应该以这种方式编写代码,但是要解决这些问题,这些问题不会阻止您运行此应用程序,并且不会更改行为,因此可能会花费更多的时间。

其他想法

这个答案解释了原始的“为什么”,但是我想为真正的问题付两美分。请注意,这只是我的意见!如果您确实有不同的想法,那完全可以。不再需要直接回答这个问题。

所有这些的根源(在我看来)是javafx api中许多代码的味道。从JavaFX组件中所有downcasting实现中的ParentHelper.ParentAccessor到所有部分的魔术字符串,经过许多大型类(甚至是上帝类),最后以不适当的亲密关系结束。本来可以更好地进行抽象,并且(例如)Node类的大小可以达到巨大的目的。

我对new release cycle寄予厚望,希望他们花费时间消除至少一些代码异味,但是我担心它们只是建立在这些代码异味的基础上,这意味着在某个时候,必须构建新的(“现代”)GUI-API。

最后

祝你有美好的一天!