≡ Menu

How to access static resources in Java

Your Java app may need access to data that you intend to ship directly with your code and which will not change without a version update. Examples might include a list of countries to show in a country dropdown, or icon images for a web or Swing application. The proper mechanism to access these files is:

YourClass.class.getResource("file.txt");
// or
YourClass.class.getResourceAsStream("file.txt");

This mechanism is built into java and will allow you to store your data files in the same place you store your class files (so, generally, on the classpath, and usually in a jar file). getResource returns a URL instance; many APIs that require a complete resource will accept these. If you wish to read the resource yourself, use getResourceAsStream, which creates an InputStream. (remember to always close it with a try/finally block or @Cleanup!)

Example code

Here’s some example code to read an image icon for Swing:

package example1;

import javax.swing.*;
import java.net.URL;

public class GetResourceExample {
    public static JLabel createPrintIcon() {
        URL printIconUrl =
                GetResourceExample.class.getResource("icons/printer63.png");
        ImageIcon printIcon =
                new ImageIcon(printIconUrl, "Print document");
        return new JLabel(printIcon);
    }

    public static void main(String... args) {
        JFrame frame = new JFrame("GetResourceExample");
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.add(createPrintIcon());
        frame.pack();
        frame.setVisible(true);
    }
}

In the above example, take the place GetResourceExample.class is located , and make sure a subdirectory called icons is also located there, which contains the printer63.png file. GetResourceExample.class is in a directory somewhere on your classpath, or in a jar. printer63.png should be in the same place.

Here’s an example to read a list of all states in the US:

package example2;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class PlainJavaStates {
    private static final List<String> states;

    static {
        List<String> out = new ArrayList<>();
        try (InputStream in =
                     PlainJavaStates.class.getResourceAsStream("states.txt")) {
            BufferedReader br =
                    new BufferedReader(new InputStreamReader(in, "UTF-8"));
            for (String line = br.readLine();
                 line != null;
                 line = br.readLine()) {
                if (line.isEmpty() || line.startsWith("#")) continue;
                out.add(line);
            }
        } catch (IOException e) {
            // RuntimeException is fine; the 'states'
            // file not existing is as likely as your States.class
            // file not existing; your app can crash
            // in the face of corrupt executables, which is what's happened
            // if states.txt isn't here.
            throw new RuntimeException("states.txt cannot be loaded.", e);
        }
        states = Collections.unmodifiableList(out);
    }

    public static List<String> getStates() {
        return states;
    }

    public static void main(String[] args) {
        System.out.printf("Number of states: %d%n ", getStates().size());
    }
}

Or, if you use Guava, this becomes even simpler:

package example2;

import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import com.google.common.io.Resources;

import java.io.IOException;

public class GuavaStates {
    private static final ImmutableList<String> states;

    static {
        try {
            states = ImmutableList.copyOf(
                    Resources.readLines(
                            GuavaStates.class.getResource("states.txt"),
                            Charsets.UTF_8));
        } catch (IOException e) {
            // RuntimeException is fine; the 'states'
            // file not existing is as likely as your States.class
            // file not existing; your app can crash
            // in the face of corrupt executables, which is what's happened
            // if states.txt isn't here.
            throw new RuntimeException("states.txt cannot be loaded.", e);
        }
    }

    public static ImmutableList<String> getStates() {
        return states;
    }

    public static void main(String[] args) {
        System.out.printf("Number of states: %d%n ", getStates().size());
    }
}

How do those paths work?

Files are resolved relative to the location of the class file you use as context. In the ‘states’ example, states.txt is supposed to be located in the exact same place in the classpath as PlainJavaStates.class. You can also start this path with a slash to resolve relative to the root of the relevant classpath entry. For example, if your jar file looks like:

/META-INF/MANIFEST.MF
/example2/PlainJavaStates.class
/example2/states.txt
/example2/foo/bar.txt
/icons/printer63.png

Then you can get to these files in any of these ways:

PlainJavaStates.class.getResource("states.txt");
PlainJavaStates.class.getResource("foo/bar.txt");
PlainJavaStates.class.getResource("/example2/states.txt");
PlainJavaStates.class.getResource("/example2/foo/bar.txt");
PlainJavaStates.class.getResource("/icons/printer63.png");

Why not files?

You could use FileInputStream to just read files from somewhere, but then you would need to know exactly where your files were. The JVM starts in the ‘working directory’, which may not be the same place your jars or classpath is, and at any rate, this does not work if you ever decide to modularize your application. It is possible to figure out where your jar file is and read from there, but that’s even more effort and easy to do wrong. Why bother?

Why not the other ways to use .getResource / .getResourceAsStream

You may have read about one of these alternate forms:

getClass().getResource(...);
getClass().getResourceAsStream(...);

This is not a good idea; getClass() is dynamic and resolves to the actual class of the instance. If your class is extended by some class that lives in another jar, that means all of a sudden your resources will no longer be found. You may also have read about this alternate form:

ClassLoader.getSystemClassLoader().getResource(...);

or:

getClass().getClassLoader().getResource(...);
MyClass.class.getClassLoader().getResource(...);

Do not use those forms either; the Java specification states that the class loader of certain classes can be null. That would mean a NullPointerException will occur. Also, the above forms are needlessly wordier. Just use this one form, there is no reason to use anything else:

MyClass.class.getResource(...);
MyClass.class.getResourceAsStream(...);

How do I get my resource files into my jars?

Eclipse will automatically copy any non-java files that are located in the same place your source files are, to the binary directory, and thus they’ll be found by your app when running it inside your IDE. Eclipse will also package these files along with your compiled code if you tell Eclipse to make jars.

With Maven or Gradle (or any build system that follows Maven’s source directory structure), put your resources in the src/main/resources directory, using the same path structure as in your src/main/java directory. Your build system will ensure your resources and your class files end up together in the same jar file, and that it’ll work out when you tell your build system to run your application.

With Ant or Ant+Ivy, you have to explicitly add your resources. You can use the <copy> task for this, or just list them as an <include> in your <jar> task.

Lastly, you could simply put the place that holds your resource files on the classpath when you start the JVM.


If you’re interested in seeing a working example of the project layout, this site has a zipped maven project available.

Comments on this entry are closed.