加载资源可以用绝对路径和相对路径,但都有局限性。用硬编码的方式不好。
比较好的方式是绝对+相对方式。即绝对路径就是应用程序安装的路径,相对路径是资源文件相对于程序根目录的路径。
java.lang.ClassLoader不仅可以加载类到内存中,还可以加载其它的资源。
1. ClassLoader.getResourceAsStream
比方说配置文件,很多时候都是放在源代码路径下的,这样就可以直接使用ClassLoader
package com.john.basis; public class MyClass { InputStream inputStream = MyClass.class.getClassLoader().getResourceAsStream("com/john/basis/conf.properties"); }
这里的conf.properties是放在com.john.basis包下的,编译的时候IDE会帮我们把资源文件拷贝到生成目录。
ClassLoader的getResourceAsStream的操作分成两个部分:getResource()和openStream()。
getSystemResourceAsStream亦是如此。
下面就以getResourceAsStream为例:
public abstract class ClassLoader { public InputStream getResourceAsStream(String name) { URL url = getResource(name); try { return url != null ? url.openStream() : null; } catch (IOException e) { return null; } } 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; } // Sub classes need to override this method protected URL findResource(String name) { return null; } }
如果parent存在,则从parent中getResource();否则从启动引导路径中去找。
如果url还是null,则调用自定义的findResource()去找。
2. Class.getResourceAsStream
jdk帮我们简化了上面的代码,如果conf.properties是和MyClass在同一个目录下,那么可以直接调用类的getResourceAsStream方法:
InputStream inputStream = MyClass.class.getResourceAsStream("conf.properties");
其实Class类的getResourceAsStream(name)也是调用了ClassLoader.getResourceAsStream(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); }
如果当前的ClassLoader为null,就调用ClassLoader.getSystemResourceAsStream(name)方法。
这里对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; }
如果name不是以'/'开头:
替换包名中的.为/,将处理后的字符串作为前缀加到name上去。在这里就是:"com/john/basis" + "/" + "conf.properties"
否则,认为该文件存放在代码根目录,去除首字符'/'。