Class.getResource和ClassLoader.getResource的区别分析
Java中取资源时,经常用到Class.getResource和ClassLoader.getResource,这里来看看他们在取资源文件时候的路径问题。
1.Class.getResource(String path)Class.getResource(String path)
path不以'/'开头时,默认是从此类所在的包下取资源;
path以'/'开头时,则是从项目的ClassPath根下获取资源。
在这里'/'表示ClassPath
JDK设置这样的规则,是很好理解的,path不以'/'开头时,我们就能获取与当前类所在的路径相同的资源文件,而以'/'开头时可以获取ClassPath根下任意路径的资源。
什么意思呢?看下面这段代码的输出结果就明白了:
package com.ppl.test;
/**
* Java中取资源时,经常用到Class.getResource和ClassLoader.getResource,
* 这里来看看他们在取资源文件时候的路径问题。
* @author ljl
*
*/
public class JavaGetResourcePathTest {
public static void main(String[] args) {
//1.Class.getResource(String path)
System.out.println(JavaGetResourcePathTest.class.getResource(""));
System.out.println(JavaGetResourcePathTest.class.getResource("/"));
//2.Class.getClassLoader().getResource(String path)
// JavaGetResourcePathTest t=new JavaGetResourcePathTest();
// System.out.println(t.getClass());
// System.out.println(t.getClass().getClassLoader());
// System.out.println(t.getClass().getClassLoader().getResource(""));
// System.out.println(t.getClass().getClassLoader().getResource("/"));
//System.out.println(JavaGetResourcePathTest.class.getClassLoader().getResource(""));
//System.out.println(JavaGetResourcePathTest.class.getClassLoader().getResource("/"));
}
}
输出结果:
file:/D:/JavaWorkSpace/ppltools/build/classes/com/ppl/test/
file:/D:/JavaWorkSpace/ppltools/build/classes/
file:/D:/JavaWorkSpace/ppltools/build/classes/
上面说到的【path以’/'开头时,则是从ClassPath根下获取;】在这里就是相当于bin目录(Eclipse环境下)。
再来一个实例,假设有如下Project结构:
package testpackage;
public class TestMain {
public static void main(String[] args) {
// 当前类(class)所在的包目录
System.out.println(TestMain.class.getResource(""));
// class path根目录
System.out.println(TestMain.class.getResource("/"));
// TestMain.class在<bin>/testpackage包中
// 2.properties 在<bin>/testpackage包中
System.out.println(TestMain.class.getResource("2.properties"));
// TestMain.class在<bin>/testpackage包中
// 3.properties 在<bin>/testpackage.subpackage包中
System.out.println(TestMain.class.getResource("subpackage/3.properties"));
// TestMain.class在<bin>/testpackage包中
// 1.properties 在bin目录(class根目录)
System.out.println(TestMain.class.getResource("/1.properties"));
}
}
1.Class.getClassLoader().getResource(String path)
path不能以'/'开头时,path是指类加载器的加载范围,在资源加载的过程中,使用的逐级向上委托的形式加载的,'/'表示Boot ClassLoader中的加载范围,因为这个类加载器是C++实现的,所以加载范围为null。如下所示:
package com.ppl.test;
/**
* Java中取资源时,经常用到Class.getResource和ClassLoader.getResource,
* 这里来看看他们在取资源文件时候的路径问题。
*
* @author ljl
*
*/
public class JavaGetResourcePathTest {
public static void main(String[] args) {
// 1.Class.getResource(String path)
// System.out.println(JavaGetResourcePathTest.class.getResource(""));
// System.out.println(JavaGetResourcePathTest.class.getResource("/"));
// 2.Class.getClassLoader().getResource(String path)
JavaGetResourcePathTest t = new JavaGetResourcePathTest();
System.out.println(t.getClass());
System.out.println(t.getClass().getClassLoader());
System.out.println(t.getClass().getClassLoader().getResource(""));
System.out.println(t.getClass().getClassLoader().getResource("/"));
// System.out.println(JavaGetResourcePathTest.class.getClassLoader().getResource(""));
// System.out.println(JavaGetResourcePathTest.class.getClassLoader().getResource("/"));
}
}
输出结果:
class com.ppl.test.JavaGetResourcePathTest
sun.misc.Launcher$AppClassLoader@73d16e93
file:/D:/JavaWorkSpace/ppltools/build/classes/
null
sun.misc.Launcher$AppClassLoader@73d16e93
file:/D:/JavaWorkSpace/ppltools/build/classes/
null
从结果来看【TestMain.class.getResource("/") == t.getClass().getClassLoader().getResource("")】
其实,Class.getResource和ClassLoader.getResource本质上是一样的,都是使用ClassLoader.getResource加载资源的。下面请看一下jdk的Class源码:
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和ClassLoader.getResource本质上是一样的。至于为什么Class.getResource(String path)中path可以'/'开头,是因为在name = resolveName(name);进行了处理:
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;
}
如果有同样的Project结构
使用Class.getClassLoader().getResource(String path)可以这么写:
package testpackage;
public class TestMain {
public static void main(String[] args) {
TestMain t = new TestMain();
System.out.println(t.getClass().getClassLoader().getResource(""));
System.out.println(t.getClass().getClassLoader().getResource("1.properties"));
System.out.println(t.getClass().getClassLoader().getResource("testpackage/2.properties"));
System.out.println(t.getClass().getClassLoader().getResource("testpackage/subpackage/3.properties"));
}
}