1、Class.getResourceAsStream 和 ClassLoader.getResourceAsStream的区别
(1)Class.getResourceAsStream:
path 不以’/'开头时默认是从此类所在的包下取资源,以’/'开头则是从classpath下获取。其只是通过path构造一个绝对路径,最终还是由ClassLoader获取资源。
public InputStream getResourceAsStream(String name) {
//解析name的路径,若不以‘/’开头,则拼接上该类所在的包路径
//如: com.abc.Test.class ,则会拼接为 com/abc/name
name = resolveName(name);
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResourceAsStream(name);
}
return cl.getResourceAsStream(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;
}
(2)ClassLoader.getResourceAsStream:
默认则是从ClassPath根下获取,path不能以’/'开头,最终是由ClassLoader获取资源。
public class TestC {
@Test
public void test1(){
InputStream resourceAsStream = TestC.class.getResourceAsStream("123.properties");
InputStream resourceAsStream1 = TestC.class.getClassLoader().getResourceAsStream("testresource/a/123.properties");
System.out.println(resourceAsStream);
System.out.println(resourceAsStream1);
}
}
运行结果:
java.io.BufferedInputStream@2d6a9952
java.io.BufferedInputStream@22a71081
2、Class.class.getResource 和 ClassLoader.getResource:
(1)Class.class.getResource: path 不以’/'开头时默认是从此类所在的包下取资源,以’/'开头则是从classpath下获取。其只是通过path构造一个绝对路径,最终还是由ClassLoader获取资源。
@Test
public void test() {
URL resource = TestC.class.getResource("");
System.out.println(resource);
URL resource1 = TestC.class.getClassLoader().getResource("");
System.out.println(resource1);
}
运行结果:
file:/E:/code/test2/target/classes/testresource/a/
file:/E:/code/test2/target/classes/
3、关于Class.class.getResource的详解
先贴源码
//父加载器
private final ClassLoader parent;
public URL getResource(String name) {
URL url;
if (parent != null) {
//双亲委派模式,先让父加载器去加载
url = parent.getResource(name);
} else {
//到达系统启动类加载器
url = getBootstrapResource(name);
}
if (url == null) {
//若父加载器找不到,则由当前类加载器尝试加载
url = findResource(name);
}
return url;
}
试验以上结论:
首先,创建了一个项目,里面仅包含两个文件,并将其打成jar包,放入扩展类加载器的目标目录中
@Test
public void test() {
URL resource = TestC.class.getResource("/test1.xml");
System.out.println(resource);
URL resource1 = TestC.class.getClassLoader().getResource("test1.xml");
System.out.println(resource1);
}
运行结果:
jar:file:/D:/worksoft/java/jre/lib/ext/test3-1.0-SNAPSHOT.jar!/test1.xml
jar:file:/D:/worksoft/java/jre/lib/ext/test3-1.0-SNAPSHOT.jar!/test1.xml
补充一下java类加载器相关知识:
启动类加载器(Bootstrap/Primordial/NULL ClassLoader):顶层的类加载器,没有父类加载器。负责加载 /lib 目录下的,或则被 -Xbootclasspath 参数所指定路径中的,并被 JVM 识别的(仅按文件名识别,如 rt.jar,名字不符合的类库即使放在 lib 目录也不会被加载)类库加载到虚拟机内存中。所有被 Bootstrap classloader 加载的类,它的
Class.getClassLoader
方法返回的都是null
,所以也称作 NULL ClassLoader。扩展类加载器(Extension CLassLoader):由
sun.misc.Launcher$ExtClassLoader
实现,负责加载<JAVA_HOME>/lib/ext
目录下,或被java.ext.dirs
系统变量所指定的目录下的所有类库;应用程序类加载器(Application/System ClassLoader):由
sun.misc.Launcher$AppClassLoader
实现。它是ClassLoader.getSystemClassLoader()
方法的默认返回值,所以也称为系统类加载器(System ClassLoader)。它负责加载 classpath 下所指定的类库,如果应用程序没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。