getClass().getResource()与getClassLoader().getResource()的区别
简介
项目中我们经常要获取资源路径,我们会使用类名.getClass().getResource()和getClassLoader().getResource()。这两个经常乱用,用着用着就迷了,
有些时候路径获取的乱七八糟,为什么没有获取到,或是获取到的不对,我们来看下,我们随便定义一个类,然后对比下输出的路径。
而且,windows下的路径和linux路径规则,大不一样,很有可能在windows下测试好好的,然后改成linux下的路径,放到服务器上就出问题了。
测试结果
测试项目的路径test/java下:
package my;
import my.son.Son;
public class Father {
protected void ask(){
System.out.println("I am Father");
}
public void ask2(){
ask();
}
public static void main(String[] args) {
Father father = new Father();
System.out.println(father.getClass().getResource(""));
System.out.println(father.getClass().getResource("/"));
System.out.println(father.getClass().getClassLoader().getResource(""));
System.out.println(father.getClass().getClassLoader().getResource("/"));
}
}
在包名my下,我们新建一个类Father,然后查看运行结果。
我们看到第二个和第三个路径一样,第一个路径多了一个包名,最后一个是null没有获取到路径。
#源码分析
我们来看下Class.getResource的源码,
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);
}
这里最终调用的还是ClassLoader.getResource()方法,但是在调用之前,有个resolveName的处理。
/**
如果名称是绝对路径,那么就删除前面的“/”,如果不是绝对路径,那么就添加包名前缀。
*/
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();
int index = baseName.lastIndexOf('.');
if (index != -1) {
name = baseName.substring(0, index).replace('.', '/')
+"/"+name;
}
} else {
name = name.substring(1);
}
return name;
}
这段代码的逻辑是处理name中的路径:
1.如果是绝对路径(也就是以“/”开头),那么就删除“/”,这里要注意了,在windows下绝对路径是有盘符概念的,
但是在linux或mac下,绝对路径就是“/”开头的,
所以如果在windows下测试没有问题,那么在linux下可能会有问题。
2.如果不是绝对路径,那么就获取包名,添加上包名(class的类对象的包名,列子中是Father的包名)。
Resources资源目录
我们在项目Resources资源目录下,创建一个123.txt,来看下程序寻找路径的结果。
代码如下
System.out.println(father.getClass().getResource("123.txt"));
System.out.println(father.getClass().getResource("/123.txt"));
System.out.println(father.getClass().getClassLoader().getResource("123.txt"));
System.out.println(father.getClass().getClassLoader().getResource("/123.txt"));
结果如下
- father.getClass().getResource(“123.txt”)加上了Father的包名,实际上找寻的是my/123.txt,当然找不到。
- System.out.println(father.getClass().getResource("/123.txt"))为什么能找到,因为“/123.txt”中的“/”被当作绝对路径处理,被删除了,最终找寻的是“123.txt”。
- father.getClass().getClassLoader().getResource(“123.txt”)找寻的就是123.txt,所以能找到,不解释。
- father.getClass().getClassLoader().getResource("/123.txt")找寻的是/123.txt,所以找不到。
最终的问题
为什么在Resources目录下,能够找寻的到?
System.out.println("sun.boot.class.path----->"+System.getProperty("sun.boot.class.path"));
System.out.println("java.ext.dirs----->"+System.getProperty("java.ext.dirs"));
System.out.println("java.class.path----->"+System.getProperty("java.class.path"));
上面三个是三个类加载器的加载路径,Resources的路径包含在“java.class.path”下,可以打印看看。
总结
1.项目中不要使用getClass().getResource,因为有绝对路径的转化及包名的问题。
2.最好使用getClassLoader().getResource,没有以上的问题,不过不建议使用绝对路径。
3.如果getClassLoader().getResource加载不到,请检查系统属性java.class.path是否包含路径。
更多内容请看个人博客:请点击此处。