【问题标题】:Exported JAR unable to read local file导出的 JAR 无法读取本地文件
【发布时间】:2016-08-21 04:37:38
【问题描述】:

我的 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 中。

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

【问题讨论】:

  • 将程序导出为 JRE 是什么意思? JRE 通常代表您用来运行 java 程序的程序,例如。 G。 java 命令是 JRE 的一部分。我也看不出它与FileInputStream 有什么关系,FileInputStream 只是一个普通的旧文件阅读器,与任何类型的文件打开功能相同。
  • 这是一个 JAR,而不是 JRE。对于那个很抱歉。所以是的,我去导出 -> 可运行的 JAR 文件 -> 等等。ObjectInputStream inputStream = new ObjectInputStream(getClass().getResourceAsStream("/asset‌​s/classes")); 但是当我在 Eclipse 中玩耍时会给出 NullPointException。

标签: java eclipse file fileinputstream objectinputstream


【解决方案1】:

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

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

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

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

my/
    package/
        MyClass.class
        assets/
            classes

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

最后但并非最不重要的一点是,如果您使用类似 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 源代码和资源放在同一个目录树中。

【讨论】:

  • 越来越近了。我已将文件移至 MyProgram/src/resources/classes(我已解压缩 JAR,它就在那里),但 getClass().getResourceAsStream("src/resources/classes") 返回 null。 (getClass().getResourceAsStream("/src/resources/classes") 也不起作用。)我在“src”中没有课程(它们都在“src/main”中)所以我怎么不使用getClass()
  • @Mark,查看我对getClass.class 的编辑。你几乎不想使用getClass 来访问资源,因为它可以被覆盖并返回一个错误的东西。
  • @Mark,至于src/main,请参阅我对 Maven 和 Gradle 的编辑。我不知道你是否使用 Maven/Gradle(也许你应该这样做,这样可以解决很多这样的问题!),但是如果你有 src/main/java,那么也许你应该在 @987654366 中拥有你的资源@。无论如何,即使您使用绝对路径,srcsrc/main 也不应该出现在 getClass 调用中,因为它们在 CLASSPATH 层次结构之上,并且绝对路径从 相同级别。
  • 所以我期待像src/main/java/my/package/MyClass.javasrc/main/resources/my/package/assets/classesMyClass.class.getResourceAsStream("assets/classes") 这样的电话。这将符合通常的模式。至少 Maven 是如何做事的,也是大多数 Java 开发人员所期望的。
  • @Marc,查看更多关于此的编辑(因为将此类内容保存在 cmets 中被认为是一种不好的风格)。
猜你喜欢
  • 2016-07-10
  • 2017-11-08
  • 1970-01-01
  • 2017-10-07
  • 1970-01-01
  • 2018-01-18
  • 1970-01-01
  • 2012-10-26
  • 1970-01-01
相关资源
最近更新 更多