Sorry, that's not right, and it's pretty easy to test, which we should have done sooner.
I create a project in eclipse. In the src directoy, I put an image file named Duke.png. I also create a package named test and create this class inside it:
package test;
import java.net.URL;
public class JarResourceTester {
public static void main(String... args){
URL url = JarResourceTester.class.getResource("Duke.png");
System.out.println(url.toString());
}
}
If you run that class, you get a NPE because it can't find Duke.png. Sound familiar? The problem is that using class.getResource() is looking *beside the class*, so it's looking inside the test package directory. We could do this instead:
package test;
import java.net.URL;
public class JarResourceTester {
public static void main(String... args){
URL url = JarResourceTester.class.getResource("../Duke.png");
System.out.println(url.toString());
}
}
This will work because it starts in the package directory next to the class, then goes up a level to the src directory. But that is gross and error-prone if we move stuff around. Plus it won't work in the jar.
This is the OP's original problem.
You could also do this:
package test;
import java.net.URL;
public class JarResourceTester {
public static void main(String... args){
URL url = JarResourceTester.class.getResource("/Duke.png");
System.out.println(url.toString());
}
}
Which works both from eclipse and from the jar. The / indicates that it's an absolute path (starting from the classpath), so it sees the file.
Eclipse outputs: file:/C:/Users/kworkman/Desktop/Tests/bin/Duke.png
Jar outputs: jar:file:/C:/Users/kworkman/Desktop/Test.jar!/Duke.png
But that seems confusing because you can mix absolute paths with paths that are relative to the class, so my preferred approach is this:
package test;
import java.net.URL;
public class JarResourceTester {
public static void main(String... args){
URL url = JarResourceTester.class.getClassLoader().getResource("Duke.png");
System.out.println(url.toString());
}
}
That way it's clear you're independent from the location of the class.
Digging into it a bit further, we can read about the logic Java is using to locate that file by reading the API for the ClassLoader.getResource() method:
Finds the resource with the given name. A resource is some data (images, audio, text, etc) that can be accessed by class code in a way that is independent of the location of the code.
The name of a resource is a '/'-separated path name that identifies the resource.
This method will first search the parent class loader for the resource; if the parent is null the path of the class loader built-in to the virtual machine is searched. That failing, this method will invoke findResource(String) to find the resource.
Parameters:
name - The resource name
Returns:
A URL object for reading the resource, or null if the resource could not be found or the invoker doesn't have adequate privileges to get the resource.
And in fact, if we look at the source code of the ClassLoader.getResourceAsStream() method, we can see that the method internally uses a URL to open a stream to it:
public InputStream getResourceAsStream(String name) {
URL url = getResource(name);
try {
return url != null ? url.openStream() : null;
} catch (IOException e) {
return null;
}
}
So, the problem has nothing to do with streams versus URLs, and everything to do with trying to use the "up one level" .. notation in a jar URL, which won't work.