Java项目中根据相对路径和绝对路径获取文件的方法 getResource(name)

首先,项目中文件分布情况如下,innerFile.txt位于test.test包下,innerInnerFile.txt位于test.test.inner包下,outterFile.txt位于包的根目录下,

文件位置

那么,在App类里,如何根据相对路径、绝对路径获取innerFile.txt、innerInnerFile.txt和outterFile.txt呢?

class.getResource(name)

先来看一下Class.getResource(name)方法,该方法接收一个表示文件路径的参数,返回一个URL对象,该URL对象表示的name指向的那个资源(文件)。这个方法是在类中根据name获取资源。其中,name可以是文件的相对路径(相对于该class类来说),也可以是绝对路径(绝对路径的话,根目录符号/是代表项目路径而不是磁盘的根目录)。例如,如下两种根据路径获取文件的效果是一样的 :

  1. App.class.getResource('innerFile.txt')
  2. App.class.getResource('/test/test/innerFile.txt')

由于innerFile.txt和App类是在同一个包下,所以通过App.class和相对路径可以获取到App这个类的包下面的innerFile.txt文件。而当传入的是绝对路径/test/test/innerFile.txt,getResource()方法是从项目的包的根目录开始解析路径的。所以这两种获取文件的效果是一样的。

classLoader.getResource(name)

该方法的作用与class.getResource(name)的作用一样,接收一个表示路径的参数,返回一个URL对象,该URL对象表示name对应的资源(文件)。但是,与class.getResource(name)不同的是,该方法只能接收一个相对路径,不能接收绝对路径如/xxx/xxx。并且,接收的相对路径是相对于项目的包的根目录来说的。比如,如下两种获取文件的效果是一样的:

  1. App.class.getResource('innerFile.txt')
  2. App.class.getClassLoader().getResource('test/test/innerFile.txt')

classLoader.getResource('test/test/innerFile.txt')是相对于项目的包的根目录来解析路径的,所以通过该路径能够获取到innerFile.txt,并且不能传入绝对路径,否则报错。
介绍完了这两种,那么应该能够根据这两种方法分别获取到innerInnerFile.txt文件和outterFile.txt文件吧?

class.getResource(name)与classLoader.getResource(name)的联系

如上所述,class.getResource(name)能够接受相对路径和绝对路径,而classLoader.getResource(name)只能接收相对路径,那么这两种方法的联系是什么呢?

通过查看class.getResource(name)源码,可以看到,class.getResource(name)最终其实是通过classLoader.getResource(name)来获取资源文件的,如,

// class.getResource(name)方法
public java.net.URL getResource(String name) {
    name = resolveName(name);
    ClassLoader cl = getClassLoader0();
    if (cl==null) {
        // A system class.
        return ClassLoader.getSystemResource(name);
    }
    return cl.getResource(name);
}

其中,class.getResource(name)首先会对name进行解析和处理,如,

// class.getResource(path)中对path预处理path = resolveName(path)
private String resolveName(String name) {
    if (name == null) {
        return name;
    }
    if (!name.startsWith("/")) {
        Class<?> c = this;
        while (c.isArray()) {
            c = c.getComponentType();
        }
        String baseName = c.getName();  // 获取带包路径的类名,此处类名是App,包的路径是test.test,所以baseName就是test.test.App。
        int index = baseName.lastIndexOf('.');
        if (index != -1) {
            name = baseName.substring(0, index).replace('.', '/')  // 给参数加上当前类所在的包的路径前缀,即在name之前加上test/test,之后会委托给classLoader来getResource(path),而classLoader默认是从项目根路径获取资源的。
                +"/"+name;
        }
    } else {
        // path以'/'开头,则去掉开头的'/',采用classLoader.getResource(去掉开头'/'后的path)来获取资源。
        name = name.substring(1);
    }
    return name;
}

其中,如果传入的是绝对路径,那么去掉最前面的/,然后会委托给classLoader.getResource(name)处理;如果传入的是相对路径,那么首先获取到当前class类的完整包名,把包名中的.替换成/,也就是说,App类获取到包名是test.test.App,那么会被替换成test/test/App,然后委托给classLoader.getResource(name)处理。

总结

class.getResource(name)中name可以写成绝对路径/test/test/innerFile.txt和相对路径【假设该class位于test.test包下】”innerFile.txt”,其中绝对路径的/是相当于项目的classpath根目录,相对路径是相对于当前class的路径。而classLoader.getResource(name)中的name一定要写成相对路径如test/test/innerFile.txt(最前面不能是/ ),并且这个相对路径是相对于项目的classpath根目录的路径,相当于class.getResource(name)中写绝对路径/test/test/innerFile.txt

喜欢的可以关注微信公众号:
这里写图片描述

参考

  1. 我自己的头条号:开发技术专注者
发布了14 篇原创文章 · 获赞 37 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/c315838651/article/details/71915742