Java中加载资源的首选方法

Java中加载资源的首选方法,java,resources,Java,Resources,我想知道用Java加载资源的最佳方法: this.getClass().getResource()(或getResourceAsStream()) Thread.currentThread().getContextClassLoader().getResource(名称) System.class.getResource(名称) 好吧,这在一定程度上取决于如果您实际上在派生类中,您希望发生什么 例如,假设超类在A.jar中,子类在B.jar中,您在超类中声明的实例方法中执行代码,但其中此引用了

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

  • this.getClass().getResource()(或getResourceAsStream())
  • Thread.currentThread().getContextClassLoader().getResource(名称)
  • System.class.getResource(名称)

好吧,这在一定程度上取决于如果您实际上在派生类中,您希望发生什么

例如,假设
超类
在A.jar中,
子类
在B.jar中,您在
超类
中声明的实例方法中执行代码,但其中
引用了
子类
的实例。如果您使用
this.getClass().getResource()
,它将与B.jar中的
子类相对。我想这通常不是必需的

就我个人而言,我可能会经常使用
Foo.class.getResourceAsStream(name)
——如果您已经知道您要查找的资源的名称,并且确定它相对于
Foo
的位置,那么这是最可靠的方法


当然,有时这也不是你想要的:根据案情判断每个案件。只是“我知道这个资源与这个类捆绑在一起”是我遇到的最常见的一个问题。

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

getResource
/
getResourceAsStream()
将从调用它的类中获得两样东西

  • 类加载器
  • 起始位置
  • 所以如果你这样做了

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

    this.getClass().getResource("/x/y/z/foo.txt")
    
    将从“this”的类加载器和x.y.z包中加载资源(它需要与该包中的类位于同一目录中)

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

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


    只需查看源代码。还表明getResourceAsStream只调用从getResource返回的URL上的“openStream”,并返回该URL。

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

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

    我知道现在回答另一个问题已经很晚了,但我只想在最后分享帮助我的东西。它还将从文件系统的绝对路径(不仅仅是类路径)加载资源/文件

    公共类资源加载器{
    公共静态URL getResource(字符串资源){
    最终列表类加载器=新的ArrayList();
    添加(Thread.currentThread().getContextClassLoader());
    add(ResourceLoader.class.getClassLoader());
    用于(类加载器类加载器:类加载器){
    最终URL=getResourceWith(类加载器,资源);
    如果(url!=null){
    返回url;
    }
    }
    最终URL systemResource=ClassLoader.getSystemResource(资源);
    if(systemResource!=null){
    返回系统资源;
    }否则{
    试一试{
    返回新文件(resource.toURI().toul();
    }捕获(格式错误){
    返回null;
    }
    }
    }
    私有静态URL getResourceWith(类加载器类加载器字符串资源){
    if(类加载器!=null){
    返回classLoader.getResource(resource);
    }
    返回null;
    }
    }
    
    我尝试了上面建议的许多方法和功能,但它们在我的项目中不起作用。总之,我找到了解决方案,现在是:

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

    skeet:如果我们是在超类的实例方法中执行语句,那么“您正在超类的实例方法中执行代码,但这指的是子类的实例”中的疑问是“this”将引用超类而不是子类。@Suresh:不,不会。试试看!创建两个类,使一个从另一个派生,然后在超类中打印输出
    this.getClass()
    。创建子类的实例并调用该方法…它将打印出子类的名称,而不是超类的名称。谢谢,子类实例方法调用了超类的方法。我想知道的是,使用this.getResourceAsStream是否只能从与this clas来源的jar相同的jar加载资源,而不是从同一jar加载资源我是另一个jar。据我估计,是类加载器加载了资源,当然不会被限制只从一个jar加载?AIK包名称不重要,是类加载器的类路径起作用。@Bart如果你看源代码,你会注意到在类上调用getResource时,类名确实很重要。The此调用所做的第一件事是调用“resolveName”,如果合适,它会添加包前缀。resolveName的Javadoc是“如果名称不是绝对的,则添加包名称前缀删除前导”/“如果名称是绝对的”啊,我明白了。这里绝对的意思是相对于类路径,而不是绝对文件系统。我只想补充一点,您应该始终检查从getResourceAsStream()返回的流不为null,因为如果资源不在类路径内,则为null。另外值得注意的是,使用上下文类加载器允许在运行时通过
    线程#setContextClassLoader
    更改类加载器。如果在程序执行时需要修改类路径,这非常有用。谢谢,这是一个好主意在我需要的时候。我正在查看代码中的注释,最后一条听起来很有趣。不是所有的资源都是从类路径加载的吗?ClassLoader.getSystemResource()会涵盖哪些情况
    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);
    }
    
    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;
        }
    
    }
    
    try {
        InputStream path = this.getClass().getClassLoader().getResourceAsStream("img/left-hand.png");
        img = ImageIO.read(path);
    } catch (IOException e) {
        e.printStackTrace();
    }