因为要在程序里面载入一些文件:比如图片,声音,配置文件等等。这些东西往往是不能用绝对路径的,否则换了机器就跑不了了。所以通过ClassLoader像装载类一样装载进来就没关系了。
现在我有一个Web Application,有一个配置文件放在WEB-INF/classes下面。用下面第一句话找不到我的东西,而第二句话可以。Why?
ClassLoader.getSystemResourceAsStream();
Thread.currentThread().getContextClassLoader().getResourceAsStream();
Java 的ClassLoader API告诉我们Java在装载类之前会先创建一个Default System ClassLoader,是其他的ClassLoader instances的总代理(Delegate),相当于父类。子类的ClassLoader找东西先通过父类找,然后再自己找。一般情况下,Java程序只有一个ClassLoader,特殊情况下有多个,比如web application。Web application的Default System ClassLoader是总来装载Tomcat的类,而不是我们写的application的类。然后Tomcat会创建新的ClassLoader来load我们自己写的类,也就是WEB-INF/classes下面的类。
综上所述,我通过第一句话拿到的一定是那个Default System ClassLoader,它不知道WEB-INF/classes,所以拿不到。而Tomcat创建的那个ClassLoader把WEB-INF/classes,WEB-INF/lib加入了Class Path,所以能找到。这里有一点要注意的是虽然WEB-INF/lib也在这个Class Path里面,但它是以“.jar”为单位的。如果你放一个“.properties”在WEB-INF/lib下面是找不到的,应该放在WEB-INF/classes下面。但是如果你把“.properties”放在jar里面就能找到了。
所以,要通过ClassLoader那东西关键是要找对ClassLoader并且调用正确的方法。ClassLoader的静态方法的主体都是那个Default System ClassLoader。一般情况下,
getClass().getResourceAsStream();
getClass().getClassLoader().getResourceAsStream();
Thread.currentThread().getContextClassLoader().getResourceAsStream();
效果是一样的。而
ClassLoader.getSystemResourceAsStream();
ClassLoader.getSystemClassLoader().getResourceAsStream();
是一样的。