导出的 JAR 无法读取本地文件

Exported JAR unable to read local file

我的 Java 应用程序在名为 "assets" 的顶级文件夹中有一个名为 "classes" 的本地文件(一个简单的 List<String>)。当我从 Eclipse 中 运行 下面的代码时,文件读取没有问题。

try { // read in classes from local file
    ObjectInputStream inputStream =
            new ObjectInputStream(new FileInputStream("assets\classes".replace("\", "/")));
    listOfClasses = (List<String>) inputStream.readObject();
    System.out.println("*** Reading local copy of classes file ***");
    inputStream.close();
} catch (FileNotFoundException e) { e.printStackTrace();
} catch (ClassNotFoundException e) { e.printStackTrace();
} catch (IOException e) { e.printStackTrace(); }

当我将我的应用程序导出为 JAR 并 运行 它在另一台计算机上时,我在第 3 行得到 java.io.FileNotFoundException。我将 "assets" 文件夹添加到构建路径,所以它应该在 JAR 中。

有人知道这里出了什么问题吗?

FileOutputStream 只是打开一个本地文件。它应该是绝对的(/home/user/...c:\temp\...)或相对于当前目录。例如,在 Windows 上,默认情况下这是包含 JAR 的目录。

如果您打算将 classes 文件 打包到 JAR 中,那么您应该使用 getResourceAsStream,如@guleryuz 所述。该路径应该是从 CLASSPATH 根(即 JAR 文件的根)开始的绝对路径,如 "/asset‌​s/classes",或者(更好)相对于调用包的路径 class.

说到 classes,我强烈建议不要使用 getClass(),因为它是一种多态方法。有两种获取 class 的方法:通过在对象上调用 getClass() 或访问静态 class “字段”。如果您有一个名为 myObjectMyClass 实例,那么 myObject.getClass()MyClass.class 本质上是同一件事。但!如果您的 class 被来自另一个包的 class 覆盖,例如 TheirClass,那么对于 TheirClass 的实例,getClass() 将 return TheirClass.class,即使您从 您的 代码调用 getClass()。所以,除非你的 class 被声明为 final,否则使用 getClass().getResource... 总是一个坏主意,因为你永远不知道什么时候有人会覆盖你的 class。而且你不应该知道甚至不关心!

考虑到所有这些,例如,您在名为 my.package 的包中有一个名为 MyClass 的 class。 JAR 可以具有以下结构:

my/
    package/
        MyClass.class
        assets/
            classes

然后,调用 MyClass.class.getResourceAsStream("assets/classes") 应该可以解决问题。或者,也可以是 MyClass.class.getResourceAsStream("/my/package/assets/classes")。请注意,绝对路径 而不是 从项目目录开始,您可能在其中有 srcsrc/main 或您的构建系统使用的任何内容。它是相对于 CLASSPATH 根的(没错,absolute 路径相对于 CLASSPATH 根是 relative,即使这听起来令人困惑)。也就是说,它从你的包所在的目录开始。

最后但并非最不重要的一点是,如果您使用类似 Maven 的构建系统(例如 Maven 或 Gradle),那么 Java 源将转到 src/main/java,但是像你的文件这样的资源文件会转到 src/main/resources。它们最终位于您的 JAR 文件中的完全相同的位置。即src/main/java/package/MyClass.java编译成package/MyClass.classsrc/main/resources/package/assets/classes编译成package/assets/classes。但是为了让构建系统弄清楚如何处理您的文件,您必须将它们放入正确的目录中:*.java 转到 java,资源转到 resources.

因此,假设采用类似 Maven 的约定,项目结构应该是

src/
    main/
        java/
            my/
                package/
                    MyClass.java
src/
    main/
        resources/
            my/
                package/
                    assets/
                        classes

给定这样的结构,您将得到一个具有上述结构的 JAR 文件。看起来有点疯狂,但一旦你掌握了它就会非常有用。

如果您没有使用类似 Maven 的构建系统,那么我不知道如何配置 Eclipse。不同的 IDE 可能有自己的约定,我一直将 Maven 与 Eclipse 结合使用。例如,一些 IDE 喜欢将 Java 源和资源放入同一目录树中。