tags:

views:

109

answers:

3

I would like to know the best way of loading a resource in Java:

  • this.getClass().getResource() (or getResourceAsStream()),
  • Thread.currentThread().getContextClassLoader().getResource(name),
  • System.class.getResource(name).
+5  A: 

Well, it partly depends what you want to happen if you're actually in a derived class.

For example, suppose SuperClass is in A.jar and SubClass is in B.jar, and you're executing code in an instance method declared in SuperClass but where this refers to an instance of SubClass. If you use this.getClass().getResource() it will look relative to SubClass, in B.jar. I suspect that's usually not what's required.

Personally I'd probably use Foo.class.getResourceAsStream(name) most often - if you already know the name of the resource you're after, and you're sure of where it is relative to Foo, that's the most robust way of doing it IMO.

Of course there are times when that's not what you want, too: judge each case on its merits. It's just the "I know this resource is bundled with this class" is the most common one I've run into.

Jon Skeet
@jon skeet: a doubt in the statement "you're executing code in an instance method of SuperClass but where this refers to an instance of SubClass" if we are executing statements inside instance method of super class then "this" will refer to the superclass not the subclass.
Suresh S
@Suresh: No it won't. Try it! Create two classes, making one derive from the other, and then in the superclass print out `this.getClass()`. Create an instance of the subclass and call the method... it'll print out the name of the subclass, not the superclass.
Jon Skeet
@jon thanks subclass instance method calls the method of superclass.
Suresh S
What I'm wondering about is whether using this.getResourceAsStream will only be able to load a resource from the same jar as the this clas is from and not from another jar. By my reckoning, it's the class loader that loads the resource and surely won't be confined from loading from only the one jar?
Michael Wiles
+2  A: 

Work out the solution according to what you want...

There are two things that getResource/getResourceAsStream() will get from the class it is called on...

  1. The class loader
  2. The starting location

So if you do

this.getClass().getResource("foo.txt");

it will attempt to load foo.txt from the same package as the "this" class and with the class loader of the "this" class. If you put a "/" in front then you are absolutely referencing the resource.

this.getClass().getResource("/x/y/z/foo.txt")

will load the resource from the class loader of "this" and from the x.y.z package (it will need to be in the same directory as classes in that package).

Thread.currentThread().getContextClassLoader().getResource(name)

will load with the context class loader but will not resolve the name according to any package (it must be absolutely referenced)

System.class.getResource(name)

Will load the resource with the system class loader (it would have to be absolutely referenced as well, as you won't be able to put anything into the java.lang package (the package of System).

Just take a look at the source. Also indicates that getResourceAsStream just calls "openStream" on the URL returned from getResource and returns that.

Michael Wiles
Thanks for this clear explanation.
PomCompot
AFAIK packages names don't matter, it's the classpath of the class loader that does.
Bart van Heukelom
@Bart if you look at the source code you'll notice that the class name does matter when you call getResource on a class. The first thing this call does is call "resolveName" which adds the package prefix if appropriate. Javadoc for resolveName is "Add a package name prefix if the name is not absolute Remove leading "/" if name is absolute"
Michael Wiles
Ah, I see. Absolute here means relative to the classpath, rather than filesystem absolute.
Bart van Heukelom
A: 

I search three places as shown below. Comments welcome.

public URL getResource(String resource){

    URL url ;

    //Try with the Thread Context Loader. 
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    if(classLoader != null){
        url = classLoader.getResource(resource);
        if(url != null){
            return url;
        }
    }

    //Let's now try with the classloader that loaded this class.
    classLoader = Loader.class.getClassLoader();
    if(classLoader != null){
        url = classLoader.getResource(resource);
        if(url != null){
            return url;
        }
    }

    //Last ditch attempt. Get the resource from the classpath.
    return ClassLoader.getSystemResource(resource);
}
dogbane