Max Mcgregor Max Mcgregor - 12 days ago 5
Java Question

Changing from FileReader to getResourceAsStream

enter image description hereWhen I tried loading my little game from a .jar file the fileReader would no longer work and I was advised to use the getResourceAsStream function instead, when I run the code bellow in the IDE it works fine so the path is correct, when I change to the getResourceAsStream which is currently commented out and remove the file reader line, I get these errors.

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


Is there a way that I can fix this?

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>

Answer

You need to start understanding what you're doing, what the classpath is, how Java loads classes, and how an app is bundled.

When you start a Java program, you use a command looking like this:

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

Given the above command, java will try loading the main class (and all the other classes used in your code) in two locations:

  • the /some/directory directory
  • the jar file /some/file.jar

Since the class is named Main, and is in the package com.foo.bar, it will thus look for a file named /some/directory/com/foo/bar/Main.class and if it doesn't find it, it will look inside the jar file for an entry /com/foo/bar/Main.class.

When you use SomeClass.getResourceAsStream("/res/worlds/world1.txt"), it will apply the same strategy: look for a file /some/directory/res/worlds/world1.txt and if not found, look inside the jar file for an entry /res/worlds/world1.txt.

So, since you use an IDE to run the project, you need to understand how an IDE works: what command does it use to start your program. The principle is quite simple:

  • there is a target directory where the IDE puts the .class files it generates from the source .java files. This target directory is part of the classpath when running the program for the IDE
  • there is also a target directory where the IDE stores all the files that are not .java files, and are located under a directory in IntelliJ marked as "Resources root". This target directory is also part of the classpath.

The structure in the target directory always respects the structure of the source/resource directory.

When packaging the app as a jar file, you'll end up with a jar file containing everything located under these target directories.

By default, in a basic IntelliJ project, your src directory is also a resources root. So every file you put there ends up in the target directory, and is thus available in the classpath, both when launching from the IDE, and when the target directory is packaged as a jar. You should just put the file somewhere under the src directory, and not mess with dependencies. Your txt file is part of the sources of the project, just like a Java file. It's "compiled" by the IDE by copying it to the directory, along with the .class files.