从 FileReader 更改为 getResourceAsStream

Changing from FileReader to getResourceAsStream

当我尝试从 .jar 文件加载我的小游戏时,fileReader 将不再工作,我被建议改用 getResourceAsStream 函数,当我 运行 下面的代码时IDE 它工作正常所以路径是正确的,当我更改为当前被注释掉的 getResourceAsStream 并删除文件 reader 行时,我得到这些错误。

Exception in thread "Thread-0" java.lang.NullPointerException
    at java.io.Reader.<init>(Reader.java:78)
    at java.io.InputStreamReader.<init>(InputStreamReader.java:72)

有什么办法可以解决这个问题吗?

public class Utils {
    public static String loadFileAsString(String path) {
        StringBuilder builder = new StringBuilder();

        try{
            //InputStream is = Utils.class.getResourceAsStream(path);
            //BufferedReader br = new BufferedReader(new InputStreamReader(is));
            BufferedReader br = new BufferedReader(new FileReader(path));
            String line;
            while((line = br.readLine()) != null)
                builder.append(line + "\n");

            br.close();
        }catch(IOException e){
            e.printStackTrace();
        }
        return builder.toString();
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
  <component name="NewModuleRootManager" inherit-compiler-output="true">
    <exclude-output />
    <content url="file://$MODULE_DIR$">
      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
      <sourceFolder url="file://$MODULE_DIR$/res" isTestSource="false" />
    </content>
    <orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
    <orderEntry type="sourceFolder" forTests="false" />
    <orderEntry type="module-library">
      <library>
        <CLASSES>
          <root url="file://$MODULE_DIR$/res" />
        </CLASSES>
        <JAVADOC />
        <SOURCES />
      </library>
    </orderEntry>
    <orderEntry type="module-library" exported="">
      <library>
        <CLASSES>
          <root url="file://$MODULE_DIR$/res/worlds" />
        </CLASSES>
        <JAVADOC />
        <SOURCES />
      </library>
    </orderEntry>
  </component>
</module>

您需要开始了解自己在做什么,class路径是什么,Java 如何加载 classes,以及应用程序是如何捆绑的。

当您启动 Java 程序时,您使用如下命令:

java -cp /some/directory:/some/file.jar com.foo.bar.Main

根据上述命令,java 将尝试在两个位置加载主 class(以及代码中使用的所有其他 classes):

  • /some/directory目录
  • jar 文件/some/file.jar

由于 class 名为 Main,并且在包 com.foo.bar 中,因此它将查找名为 /some/directory/com/foo/bar/Main.class 的文件,如果找不到,则它将在 jar 文件中查找条目 /com/foo/bar/Main.class.

当您使用 SomeClass.getResourceAsStream("/res/worlds/world1.txt") 时,它将应用相同的策略:查找文件 /some/directory/res/worlds/world1.txt,如果未找到,则在 jar 文件中查找条目 /res/worlds/world1.txt

因此,由于您使用 IDE 到 运行 项目,您需要了解 IDE 的工作原理:它使用什么命令来启动您的程序。原理很简单:

  • 有一个目标目录,IDE 放置它从源 .java 文件生成的 .class 文件。当 运行 为 IDE
  • 设置程序时,此目标目录是 class 路径的一部分
  • 还有一个目标目录,其中 IDE 存储所有不是 .java 文件的文件,并且位于 IntelliJ 中标记为 "Resources root" 的目录下。此目标目录也是 class 路径的一部分。

目标目录中的结构始终遵循 source/resource 目录的结构。

将应用程序打包为 jar 文件时,您最终会得到一个包含这些目标目录下所有内容的 jar 文件。

默认情况下,在基本的 IntelliJ 项目中,您的 src 目录也是资源根目录。所以你放在那里的每个文件最终都在目标目录中,因此在 class 路径中可用,无论是从 IDE 启动时,还是当目标目录被打包为 jar 时。你应该把文件放在 src 目录下的某个地方,不要弄乱依赖关系。您的 txt 文件是项目源代码的一部分,就像 Java 文件一样。 IDE 将其与 .class 文件一起复制到目录中,从而成为 "compiled"。