用Java加载资源的首选方式


107

我想知道用Java加载资源的最佳方法:

  • this.getClass().getResource() (or getResourceAsStream())
  • Thread.currentThread().getContextClassLoader().getResource(name)
  • System.class.getResource(name)

Answers:


140

根据您的需要制定解决方案...

从被调用的类中getResource/ getResourceAsStream()将获得两件事...

  1. 类加载器
  2. 起始位置

所以如果你这样做

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

它将尝试从与“ this”类相同的包中以及“ this”类的类加载器中加载foo.txt。如果在前面加上“ /”,则绝对是在引用资源。

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

将从“ this”的类加载器和xyz包中加载资源(它必须与该包中的类位于同一目录中)。

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

将使用上下文类加载器加载,但不会根据任何包解析名称(必须绝对引用)

System.class.getResource(name)

将使用系统类加载器加载资源(也必须绝对引用该资源,因为您将无法在java.lang包(System的包)中放入任何内容。

只需查看源代码即可。还指示getResourceAsStream只是在从getResource返回的URL上调用“ openStream”并将其返回。


AFAIK包名称无关紧要,这是类加载器的类路径。
Bart van Heukelom

@Bart如果您查看源代码,您会注意到在类上调用getResource时,类名称确实很重要。调用的第一件事是调用“ resolveName”,如果合适的话,它会添加包前缀。resolveName的Javadoc是“如果名称不是绝对的,则添加软件包名称前缀”;如果名称是绝对的,则删除前导“ /””
Michael Wiles 2010年

10
知道了 这里的绝对是指相对于类路径,而不是文件系统绝对。
Bart van Heukelom

1
我只想补充一点,您应该始终检查从getResourceAsStream()返回的流是否不为null,因为如果资源不在类路径中,则应为null。
stenix

另外值得注意的是,使用上下文类加载器允许在运行时通过更改类加载器Thread#setContextClassLoader。如果您需要在程序执行时修改类路径,这将很有用。
Max

14

好吧,这部分取决于如果您实际上在派生类中会发生什么。

例如,假设SuperClass在A.jar和SubClassB.jar中,并且您正在中声明的实例方法中执行代码,SuperClass但其中this引用的实例SubClass。如果使用this.getClass().getResource()它,它将相对于SubClassB.jar中的。我怀疑通常不是必需的。

我个人可能会Foo.class.getResourceAsStream(name)最常使用-如果您已经知道要使用的资源的名称,并且确定该资源相对于的位置Foo,那将是执行IMO的最可靠的方法。

当然,有时候也不是您想要的:根据每个案例的优劣来判断。只是“我知道此资源与此类捆绑在一起”是我遇到的最常见的资源。


skeet:如果“我们正在超类的实例方法内执行语句,那么” this”将引用超类而不是语句“您正在超类的实例方法中执行代码,但此处引用的是SubClass的实例”。子类。
Dead Programmer,2010年

1
@Suresh:不,不会。试试吧!创建两个类,使一个从另一个类派生,然后在超类中打印出this.getClass()。创建子类的实例并调用该方法...它将打印出子类的名称,而不是超类的名称。
乔恩·斯基特

感谢子类实例方法调用超类的方法。
Dead Programmer,2010年

1
我想知道的是,使用this.getResourceAsStream是否只能从与这个clas相同的jar中加载资源,而不能从另一个jar中加载资源。据我估计,是由类加载器加载资源,并且肯定不会仅从一个jar加载吗?
Michael Wiles 2010年

10

我搜索三个地方,如下所示。欢迎发表评论。

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);
}

谢谢,这是个好主意。正是我所需要的。
devo

2
我正在查看代码中的注释,最后一个听起来很有趣。并非所有资源都从类路径加载吗?以及在什么情况下ClassLoader.getSystemResource()可以解决以上未成功的问题?
nyxz 2015年

老实说,我不明白为什么您要从3个不同的地方加载文件。您不知道文件存储在哪里吗?
bvdb

3

我知道再来一个答案真的很晚,但我只想分享最后对我有帮助的内容。它还将从文件系统的绝对路径(不仅是类路径)的绝对路径中加载资源/文件。

public class ResourceLoader {

    public static URL getResource(String resource) {
        final List<ClassLoader> classLoaders = new ArrayList<ClassLoader>();
        classLoaders.add(Thread.currentThread().getContextClassLoader());
        classLoaders.add(ResourceLoader.class.getClassLoader());

        for (ClassLoader classLoader : classLoaders) {
            final URL url = getResourceWith(classLoader, resource);
            if (url != null) {
                return url;
            }
        }

        final URL systemResource = ClassLoader.getSystemResource(resource);
        if (systemResource != null) {
            return systemResource;
        } else {
            try {
                return new File(resource).toURI().toURL();
            } catch (MalformedURLException e) {
                return null;
            }
        }
    }

    private static URL getResourceWith(ClassLoader classLoader, String resource) {
        if (classLoader != null) {
            return classLoader.getResource(resource);
        }
        return null;
    }

}

0

我尝试了上面建议的许多方法和功能,但是它们在我的项目中不起作用。无论如何,我找到了解决方案,在这里是:

try {
    InputStream path = this.getClass().getClassLoader().getResourceAsStream("img/left-hand.png");
    img = ImageIO.read(path);
} catch (IOException e) {
    e.printStackTrace();
}

this.getClass().getResourceAsStream()在这种情况下,您最好使用。如果您看一下该getResourceAsStream方法的源代码,您会发现它的作用与您相同,但是方式更聪明(如果ClassLoader在类中找不到该方法,则可以使用备用方法)。这也表明,你可能会遇到一个潜在nullgetClassLoader在你的代码...
文件Davluz

就像我说的那样,@ PromCompot this.getClass().getResourceAsStream()对我不起作用,因此我使用了它。我认为有些人可以面对像我这样的问题。
弗拉迪斯拉夫
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.