Class 与 ClassLoader
写 java 代码时常常需要加载一些外部的资源,通常我们会使用全路径名加载一份资源,比如:C:\Users\XXX\Desktop\abc.jpg
但是,有些时候我们需要加载的是源代码路径下的资源或者配置文件等等,更习惯于使用相对路径,或者直接给一个文件名,就希望能够找到我们需要的配置文件。
如何做到?
常见的方法是使用了 class.getResource 或 classloader.getResource
并且使用绝对路径是一个很差的方式,它会导致你的程序移植性很差,尽量使用相对路径
这两个方法看起来很相似,他们直接有什么区别?
查看源代码:
Class.getResourceAsStream(String name)
public InputStream getResourceAsStream(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResourceAsStream(name);
}
return cl.getResourceAsStream(name);
}
上面的代码可以看出, Class.getResourceAsStream(String name)
最终还是调用了 classloader.getResourceAsStream(String name)
。
但是两者还是有一些区别的,注意 name =resolveName(name)
这一行, Class.getResourceAsStream(String name)
在这里做了一些处理:
Class.resolveName(String 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;
}
现在看这段代码还有点云里雾绕,不妨写几行代码测试一下,看看这段代码到底在干嘛:
public class App {
public static void main(String[] args) {
System.out.println("App.class.getClassLoader().getResource(\"\") : " + App.class.getClassLoader().getResource(""));
System.out.println("App.class.getClassLoader().getResource(\"/\") : " + App.class.getClassLoader().getResource("/"));
System.out.println("App.class.getResource(\"\") : " + App.class.getResource(""));
System.out.println("App.class.getResource(\"/\") : " + App.class.getResource("/"));
}
}
输出
App.class.getClassLoader().getResource("") : file:/D:/workspace/eclipse/cluster/TestClassloader/bin/
App.class.getClassLoader().getResource("/") : null
App.class.getResource("") : file:/D:/workspace/eclipse/cluster/TestClassloader/bin/space/yukai/
App.class.getResource("/") : file:/D:/workspace/eclipse/cluster/TestClassloader/bin/
虽然上面的代码使用了 getResource,但与 getResourceAsStream 大同小异。
可以看到,
calssloder.getResource("")
方法返回了 classpath 根路径(eclipse 工程中,编译生成的类文件存放在/bin 目录下);
calssloder.getResource("/")
方法返回 null,说明 calssloder.getResource 不支持以”/“开头的参数;
class.getResource("")
方法返回了 App.class 所在的路径;
class.getResource("/")
与 calssloder.getResource("")
表现一致,返回了 classpath 的根路径
再回顾上面的代码,是否有一点明白了呢?
读取 jar 包所在的位置
有时候需要知道 jar 包所在的位置,比如我们的项目需要一个默认的日志文件输出路径,这个路径就可以是运行时 jar 包所在的目录。如何获取 jar 包所在的目录?
URL url = App.class.getProtectionDomain().getCodeSource().getLocation();
String path = url.toURL().getPath();
注意,上面的方法仅适用 jdk1.5 及以上
附实际使用方式
1. package com.zkn.newlearn.others;
2.
3. import java.io.IOException;
4. import java.io.InputStream;
5. import java.util.Properties;
6.
7. import com.zkn.newlearn.gof.singleton.SimpleFactoryTest01;
8.
9. /**
10. * 读取资源文件的五种方式
11. * @author zkn
12. */
13.
14. public class ClassReadResourceDemo {
15.
16. public static void main(String[] args) {
17. /**
18. * 第一种方式 用类加载器读取资源文件。
19. * 适用情形:资源文件和类文件在不在同一目录都可以。
20. * 注意:getResourceAsStream里的参数要
21. * 写资源文件的全限定路径,包名+文件名
22. * 开头千万不要写"/"
23. */
24. InputStream is = ClassReadResourceDemo.class.
25. getClassLoader().getResourceAsStream("com/zkn/newlearn/io/config.properties");
26. Properties prop = new Properties();
27. try {
28. prop.load(is);
29. System.out.println(prop.getProperty("key"));
30. is.close();
31. } catch (IOException e) {
32. e.printStackTrace();
33. }
34. /**
35. * 第二种写法:用class.getResourceAsStream()(其实还是用的类加载器)
36. * 适用情形:如果资源文件和类文件在同一包下,直接写资源文件的名称就行了,
37. * 注意:资源文件的名称前面不需要加“/”
38. */
39. is = ClassReadResourceDemo.class.getResourceAsStream("config.properties");
40. try {
41. prop.load(is);
42. System.out.println(prop.getProperty("key"));
43. is.close();
44. } catch (IOException e) {
45. e.printStackTrace();
46. }
47. /**
48. * 第三种写法:用class.getResourceAsStream()(其实还是用的类加载器)
49. * 适用情形:这个写法适用的情形是资源文件和类文件不在同一个目录下的情况
50. * 注意:开头一定要加上”/“
51. */
52. is = ClassReadResourceDemo.class.getResourceAsStream("/com/zkn/newlearn/io/config.properties");
53. try {
54. prop.load(is);
55. System.out.println(prop.getProperty("key"));
56. is.close();
57. } catch (IOException e) {
58. e.printStackTrace();
59. }
60. /**
61. * 第四种写法:用class.getResourceAsStream()
62. * 适用情形:这种写法适用于资源文件在根目录下的情况
63. * 注意:文件名称前面一定要加上”/“
64. */
65. is = ClassReadResourceDemo.class.getResourceAsStream("/config.properties");
66. try {
67. prop.load(is);
68. System.out.println(prop.getProperty("key"));
69. is.close();
70. } catch (IOException e) {
71. e.printStackTrace();
72. }
73. /**
74. * 第五种写法:用类加载器来读取资源文件
75. * 适用情形:资源文件在跟目录下
76. * 注意:资源文件名称前面一定不要加”/“
77. */
78. is = ClassReadResourceDemo.class.getClassLoader().getResourceAsStream("config.properties");
79. try {
80. prop.load(is);
81. System.out.println(prop.getProperty("key"));
82. is.close();
83. } catch (IOException e) {
84. e.printStackTrace();
85. }
86. }
87. }
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于