Search code examples
javajava-8classloader

Using ClassLoader when loading .jar resources


I just don't understand why using the ClassLoader causes these two cases to act differently. Can someone explain how/why the ClassLoader changes the search such that it requires a full path into the jar?

package com.example;
import java.io.InputStream;

public class SomeClass {
  private static InputStream stm;
  static {
    for (final String s : new String[] { "file.png", "com/example/file.png", "/com/example/file.png" }) {
      // case 1 - w/o classLoader
      stm = SomeClass.class.getResourceAsStream(s);
      System.out.println("w/o          : " + (stm == null ? "FAILED to load" : "loaded") + " " + s);

      // case 2 - w/ classLoader
      stm = SomeClass.class.getClassLoader().getResourceAsStream(s);
      System.out.println("w/classloader: " + (stm == null ? "FAILED to load" : "loaded") + " " + s);
    }
  }

  public static void main(final String args[]) {}
}

Produces:

w/o          : loaded file.png
w/classloader: FAILED to load file.png
w/o          : FAILED to load com/example/file.png
w/classloader: loaded com/example/file.png
w/o          : loaded /com/example/file.png
w/classloader: FAILED to load /com/example/file.png

Solution

  • Because the path is evaluated differently depending on which getResourceAsStream() method you call.
    If you call Class.getResourceAsStream() on the class com.example.SomeClass, then the path is evaluated relatively to SomeClass if it does not start with a /, so file.png becomes /com/example/file.png and com/example/file.png becomes /com/example/com/example/file.png.
    If you call ClassLoader.getResourceAsStream() on its ClassLoader, then the path is implicitly absolute and must not start with a /, so file.png becomes /file.png and com/example/file.png becomes /com/example/file.png.
    If you use an absolute path like /com/example/file.png, you have to use the Class method.

    Unfortunately this implicit absoluteness of ClassLoader.getResourceAsStream() is only documented implicitly in the documentation of Class.getResourceAsStream(), which states that it strips the leading slash before it delegates to its ClassLoaders method.