Java 读取资源、配置文件

本贴最后更新于 2694 天前,其中的信息可能已经沧海桑田

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. }
  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1063 引用 • 3455 回帖 • 162 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...