Java NullPointerException仅适用于Linux



我正在做一个Java项目,它在Windows 10上运行良好,但当我在Ubuntu上测试时,它显示

"AWT-EventQueue-0"java.lang.NullPointerException:无法读取数组长度,因为"是零。

我读了这个答案,但是找不到解决办法。

我在项目中所做的是从某个路径加载图像数组。以下是我代码中有缺陷的部分:

BufferedImage[] allImages;
public ImageArray(String set, int n) {
File path = new File("res/mnist_png/" + set + "/" + n);
File[] allFiles = path.listFiles();
allImages = new BufferedImage[allFiles.length];

JLabel label[] = new JLabel[allFiles.length];
for (int i = 0; i < allFiles.length; i++) {
try {
allImages[i] = ImageIO.read(allFiles[i]);
label[i] = new JLabel();
ImageIcon icon = new ImageIcon(allImages[i]);

我尝试删除变量allFiles并用它持有的实际代码替换它的使用,但没有成功。我在之前的回答中看到,使用new/this关键字可以解决这个问题,但我似乎无法找到是否以及在哪里使用它们。我打印了allFilespath.listFiles()的值,它确实是空的。如果它们仍然为空,程序是否有办法工作?更改null是否会以某种方式破坏他们的预期工作?

正如我提到的,这个问题只发生在Linux上,但在Windows上可以正常工作。如果你能帮忙,我将不胜感激。

第一个问题:您使用的是相对文件名。根据Java进程的当前工作目录,相对文件名具有不同的含义。这不是Java的概念;早在Java出现之前,系统中的每个进程都有自己的当前目录。

第二个问题:您正在尝试列出应用程序资源。如果您选择将应用程序打包为.jar文件,这将不起作用,因为.jar是一个单独的归档文件;其中的数据都是一个文件的一部分,它们不构成实际的文件。

相对文件名

任何不以目录分隔符开头的文件(在大多数系统上是/,或者在Windows中是)都是一个相对文件名。相对文件名所指的实际文件位置取决于Java进程的当前目录。所有的程序,不仅仅是Java程序,都是这样工作的。

一些例子:

File name                       Current directory       Actual file location
---------                       -----------------       --------------------
res/mnist_png/A/1/image01.png   /home/gosho09/project   /home/gosho09/project/res/mnist_png/A/1/image01.png
mnist_png                       /home/gosho09/project   /home/gosho09/project/mnist_png
mnist_png                       /                       /mnist_png
/home/gosho09/project/res       /tmp                    /home/gosho09/project/res
/home/gosho09/project/res       /home/gosho09           /home/gosho09/project/res
/home/gosho09/project/res       /usr/local/bin          /home/gosho09/project/res
/var/log                        /tmp                    /var/log
/var/log                        /home/gosho09           /var/log
/var/log                        /usr/local/bin          /var/log

如您所见,如果文件名不以/开头,它是相对的,并且当前目录决定了实际位置。

如果文件名以/开头,则认为它是绝对文件名。绝对文件名不受当前目录影响。

但是,你不应该使用文件名。

如果您想要分发您的应用程序,您很可能希望将其打包为.jar文件。jar文件是一个单独的归档文件,其中包含编译过的类和应用程序资源文件,如映像集。

因为.jar文件是一个归档文件(它实际上是一个专门的zip文件),所以其中的条目只是压缩形式的归档文件的一部分。它们不是单独的文件,只是字节序列。你不能用File类读取它们。

幸运的是,有一种方法可以读取应用程序资源,当应用程序被打包为.jar文件时,以及当它以常规. Class文件和数据文件的形式存在时,这种方法都可以工作:Class。getResource方法。

典型的用法可能是这样的:

String path = "/mnist_png/" + set + "/" + n + "/image" + i + ".png");
URL imageLocation = ImageArray.class.getResource(path);
if (imageLocation == null) {
throw new RuntimeException("Missing resource "" + path + """);
}
allImages[i] = ImageIO.read(imageLocation);

您可能想知道如何在没有File类的情况下列出文件。答案是:你不能也不应该。

按照设计,不能列出应用程序资源。这是因为它们不能保证是可列出的。资源由classloader加载,classloader可以从目录或.jar文件中读取,也可以不读取。

(事实上,Java SE运行时不再以.jar文件的形式包含其核心类;因此,曾经假设这些类可以作为.jar文件提供的第三方工具必须重写。Java并没有把这些工具的开发人员扫地在外;假设类来自.jar文件从来都不被认为是安全的,那些开发人员选择不理会这个警告。

列出资源的替代方法是包含一个包含已知资源路径列表的资源。这是你的申请;你知道里面有什么。因此,只需编写一个简单的纯文本清单,将其包含在应用程序中,并从中读取:

String root = "/mnist_png/" + set + "/" + n + "/";
String listingPath = root + "image-list.txt";
try (BufferedReader listing = new BufferedReader(
new InputStreamReader(
Objects.requireNonNull(
ImageArray.class.getResourceAsStream(listingPath),
"Missing resource "" + listingPath + ""),
StandardCharsets.UTF_8))) {
List<JLabel> labelList = new ArrayList<>();
String path;
while ((path = listing.readLine()) != null) {
URL imageLocation = ImageArray.class.getResource(root + path);
if (imageLocation == null) {
throw new RuntimeException(
"Missing resource "" + root + path + """);
}
labelList.add(new JLabel(new ImageIcon(imageLocation)));
}
labels = labelList.toArray(new JLabel[0]);
}

最新更新