JavaFX 应用程序抛出 NullPointerExceptions 但仍然运行 [英] JavaFX applications throw NullPointerExceptions but run anyway

查看:31
本文介绍了JavaFX 应用程序抛出 NullPointerExceptions 但仍然运行的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

public class JavaFXTSample extends Application {@覆盖public void start(Stage primaryStage) 抛出异常 {StackPane iAmRoot = new StackPane();场景场景 = 新场景(iAmRoot, 300, 250);primaryStage.setScene(场景);primaryStage.show();}公共静态无效主(字符串 [] args){发射(参数);}}

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

 线程 [main](挂起(NullPointerException 异常))SystemProperties.setVersions() 行:81 [局部变量不可用]SystemProperties.lambda$static$28() 行:6730621981.run() 行:不可用AccessController.doPrivileged(PrivilegedAction) 行:不可用 [本机方法]SystemProperties.() 行:64LauncherImpl.startToolkit() 行:668LauncherImpl.launchApplicationWithArgs(String, String, String[]) 行:337LauncherImpl.launchApplication(String, String, String[]) 行:328NativeMethodAccessorImpl.invoke0(Method, Object, Object[]) 行:不可用 [本地方法]NativeMethodAccessorImpl.invoke(Object, Object[]) 行:不可用DelegatingMethodAccessorImpl.invoke(Object, Object[]) 行:不可用Method.invoke(Object, Object...) 行:不可用LauncherHelper$FXHelper.main(String...) 行:不可用

这是第二个:

线程 [JavaFX 应用程序线程](挂起(NullPointerException 异常))PropertyHelper.lambda$getBooleanProperty$514(String) 行:397164036.run() 行:不可用AccessController.doPrivileged(PrivilegedAction) 行:不可用 [本机方法]PropertyHelper.getBooleanProperty(String) 行:37父.<clinit>() 行:87JavaFXTSample.start(Stage) 行:16LauncherImpl.lambda$launchApplication1$162(AtomicBoolean, Application) 行:8632266602.run() 行:不可用PlatformImpl.lambda$runAndWait$175(Runnable, CountDownLatch) 行:32632251660.run() 行:不可用PlatformImpl.lambda$null$173(Runnable) 行:29511305869.run() 行:不可用AccessController.doPrivileged(PrivilegedAction, AccessControlContext) 行:不可用 [本机方法]PlatformImpl.lambda$runLater$174(Runnable, AccessControlContext) 行:29430052382.run() 行:不可用InvokeLaterDispatcher$Future.run() 行:95WinApplication._runLoop(Runnable) 行:不可用 [本机方法]WinApplication.lambda$null$148(int, Runnable) 行:19132126786.run() 行:不可用Thread.run() 行:不可用

更重要的是,如果我删除 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 私有静态无效 setVersions() {79 个整数大小;80 输入流是 =81 SystemProperties.class.getResourceAsStream(versionResourceName);第82话83 大小 = is.available();8485 字节[] b = 新字节[大小];86 int n = is.read(b);87 String inStr = new String(b, "utf-8");88 SystemProperties.setFXProperty(javafx.version",第 89 章9091 SystemProperties.setFXProperty("javafx.runtime.version",第 92 章9394 } catch(忽略异常){95 }96 }

解决方案

免责声明

这个问题已有 1 年历史,但我觉得仍然非常相关.这就是我回答这个问题的原因(详细).

我会尽力回答这个问题

TL;DR

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

<块引用>

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

第一个空指针异常

您显示的第一个异常位于:

<块引用>

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

您已经发布了相关代码.为了形象化这一点,我将再次发布.问题所在的行是:

InputStream 是 =SystemProperties.class.getResourceAsStream(versionResourceName);尝试  {大小 = is.available();//InputStream "is" = null...} catch(忽略异常){}

这很容易解释.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 中创建一个名为version.properties"的文件.资源.这个versions.properties应该看起来像这样:

release=1满=1

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

第二个空指针异常

第二个 NullPointer 位于 PropertyHelper.lambda$getBooleanProperty$514(String) line: 39 行内.这个方法看起来像这样:

static boolean getBooleanProperty(final String propName) {尝试 {boolean answer = AccessController.doPrivileged((java.security.PrivilegedAction) () -> {String propVal = System.getProperty(propName);返回真".equals(propVal.toLowerCase());//第 39 行});返回答案;} 捕获(任何例外){}返回假;}

由此得出的结论是,propVal 为空,这是由 来自 System.getProperty 的 JavaDoc

<块引用>

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

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

<块引用>

Parent.() 行:87

定义应该找到的属性.内容如下:

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

因此,前面所述的方法尝试查找 SystemProperty "javafx.sg.warn",但不检查它是否存在.这同样不会被处理,因此只能在调试器中看到.

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

修复此异常

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

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

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

-Djavafx.sg.warn="true"

这将消除在引用之前调用代码的需要.当然,您可以将 true 换成其他任何东西.但是这样一来,属性就存在了,问题行就不会抛出 NullPointerException.

为什么只执行了 Stage.show 不会抛出第二个异常?

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

您应该修复异常吗?

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

进一步的想法

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

这一切的根源(在我看来)是 javafx api 中的许多代码异味.从所有部分中的魔法字符串开始,在所有 向下转换之上.com/teamfx/openjfx-10-dev-rt/blob/master/modules/javafx.graphics/src/main/java/com/sun/javafx/scene/ParentHelper.java" rel="noreferrer">ParentHelper.ParentAccessor JavaFX 组件内的实现,经过许多大型类(甚至可能是神类),最终以不适当的亲密关系结束.抽象本来可以更好,并且(例如)Node 类的大小非常庞大.

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

终于

祝您有美好的一天!

Every JavaFX application I've run throws two NullPointerExceptions. They don't prevent or even affect the execution of the projects, and I can only see them if I run my applications in debug mode. I'm even having this issue with the HelloWorld sample from Oracle and this minimal program:

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);
    }
}

Here is the stack trace of the first error:

 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 

And here is the second:

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    

What's more, if I remove any instance of iAmRoot and scene (so start() just reads primaryStage.show();), the second error doesn't occur. Why is this happening?

I've been able to find this question before (JavaFX application throws NullPointerException at startup), but noone seems to have resolved it, and it was asked over 2 years ago.

If it helps, I'm running Eclipse 4.5.2 on Windows 7 Professional, and I don't think I'm using FXML at all.

EDIT:

for what it's worth, I can't find the source code for the second error, but I found JavaFX's code for the method which throws the first error (line 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 }

解决方案

Disclaimer

This question is 1 year old, but i feel like still very relevant. That's why i answer this question (in length).

I'll try to answer this question to the best of my knowledge

TL;DR

JavaFX has some quirky code-lines and the exceptions are cause by those. All can be traced back to some line in JavaFX, which misses a check or uses an unchecked part.

Every JavaFX application I've run throws two NullPointerExceptions. They don't prevent or even affect the execution of the projects, and I can only see them if I run my applications in debug mode.

First NullPointerException

The first Exception you shown is located at:

SystemProperties.setVersions() line: 81 [local variables unavailable]

You already posted the correlating code. To visualize this, i'll post this again. The Line in Problem is:

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

This is pretty easy explained. The getResourceAsStream method returns the following (from the JavaDoc):

Returns: A InputStream object or null if no resource with this name is found

This means, that the resource (that you again already posted) "/com/sun/javafx/runtime/resources/version.properties" is not found.

This however is not handled, but simply ignored. This exception is therefor not printed, but still is thrown. It is catched by the debugger, but that's it. Therefor only identifiable by the debugger.

The why is not really well identifiable. One possibility might be that the JDK does not contain the resource. In my tests of the JDK-10 (which i currently use), the package com.sun.javafx.runtime exists, but the sub-package resources does not. This is not really reproducible, since the com.sun package appears to not be documented. The base api of javafx is documented here, but the com.sun.javafx package is not. You can look at it, if you are using a sophisticated IDE (like IntelliJ for example). My JDK-8 does contains it. I use the Oracle JDK on Ubuntu 18.04.1.

One possibility might be, that the package has been ditched along the way. Maybe someone thought, that referencing the resource through packages is not that great and put it within the resources path, but since the exception is swallowed into nothing, this error has never been detected.

Fixing this issue

NOTE: This potential fix is NOT tested thoroughly

This issue might be fixed, if you manually introduce the resource. Considering the code you posted (and that can be found within the SystemProperties.setVersions method), you would have to create a file called "version.properties" within the package com.sun.javafx.runtime.resources. This versions.properties should look like something like this:

release=1
full=1

Those are arbitrary values which you would have to test.

Second NullPointerException

The second NullPointer is rooted within the line PropertyHelper.lambda$getBooleanProperty$514(String) line: 39. This method looks like this:

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;
}

This leads to the conclusion, that the propVal is null, which is backed by the JavaDoc from System.getProperty

Returns: the string value of the system property, or null if there is no property with that key.

This means, the propName is not found. But propName is the method argument. So, what is the propName? This again can be found within the stacktrace. The line:

Parent.() line: 87

defines the property that should be found. It reads the following:

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

So, the method stated before tries to find the SystemProperty "javafx.sg.warn", but does not check whether or not it exists. This again is not handled and therefor only visible within the debugger.

This Exception would not occur if the "toLowerCase" call would be erased.

Fixing this Exception

You can simply fix this exception, by introducing the following code before referencing the Parent class in any way:

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

It has to be done before any reference, because this attribute is static. So, if this class is referenced in any way in code, it is loaded. One possibility would be, to add a static block to your main class, which contains this code. Another way would be to add the following to the command line:

-Djavafx.sg.warn="true"

This would erase the need to call the code before references. Of course you can exchange true for anything else. But with that, the property exists and the problem line will not throw a NullPointerException.

Why is the second Exception not thrown if only Stage.show is executed?

This is pretty simple. In JavaFx any element to display is a Node within the Scene graph. Every Node may have a Parent, which is a subclass of Node. Nearly all classes extend Parent, so does the StackPane, but not the Stage. By referencing the StackPane, you reference Parent, which triggers the loading of the property. If you do not set a StackPane, you do not reference the Parent class, which means you do not trigger the loading of the property. So no Exception is thrown.

Should you fix the Exceptions?

Since the team behind javafx appears to ignore those exception, you can too. I do not say that this is good practice or you should code in this way, but fixing those issues, that do not prevent you from running this application and that do not change the behavior may cost more time than it is worth.

Further thoughts

This answer explained the raw "why", but i want to give my two cents to the real issue. Please note that this is just my opinion! If you do think differently, that's totally okay. It is no longer directly necessary to answer the question.

This root of all of this is (in my opinion) the many many code smells within the javafx api. From magic strings within all parts, over downcasting within all ParentHelper.ParentAccessor implementations within the components of JavaFX, passing by many Large-Classes (maybe even god classes) and finally ending in Inappropriate intimacy. The abstraction could have been much better and the size of (for example) the Node class is way to enormous.

With the new release cycle, my hopes are high that they take their time to eliminate at least some of those code smells, but i fear that they just build upon those code smells, which means that at some time, a new ("modern") GUI-API has to be build.

Finally

Have a nice day!

这篇关于JavaFX 应用程序抛出 NullPointerExceptions 但仍然运行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆