來看看下面代碼的結果java
package com.jdk.resource; public class Resource { public static void main(String[] args) throws Exception { System.out.println(Resource.class.getResource("")); System.out.println(Resource.class.getResource("/")); System.out.println(Resource.class.getClassLoader().getResource("")); System.out.println(Resource.class.getClassLoader().getResource("/")); } }
結果:api
file:/D:/workspace/JDKCore/bin/com/jdk/resource/ file:/D:/workspace/JDKCore/bin/ file:/D:/workspace/JDKCore/bin/ null
能夠看到Resource.class.getResource("")獲得的路徑classpath下Resource 類所在包,而Resource.class.getResource("/")爲classpath根路徑;而Resource.class.getClassLoader().getResource("")一樣爲classpath根路徑,Resource.class.getClassLoader().getResource("/")則爲空。 下面來看看Class#getResource方法的源碼this
public java.net.URL getResource(String name) { name = resolveName(name); ClassLoader cl = getClassLoader0(); if (cl==null) { // A system class. return ClassLoader.getSystemResource(name); } return cl.getResource(name); }
從 cl.getResource(name)一行能夠看出,Class#getResource方法最終仍是調用了CalssLoader#getResource方法。再來看看name = resolveName(name)的實現,作了什麼處理,其調用了Class類的resolveName方法url
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; }
能夠看出,resolveName方法的目的是當參數name以'/'開頭就將'/'去除再返回,而當不以'/'開頭則根據這個類對應的帶包名全稱變換成具體的路徑名,如com.jdk.resource替換成com/jdk/resource。spa
如今應該明白最開始那個Resource 類中main方法的結果,實際上Class#getResource方法是去調CalssLoader#getResource的方法,只是在調用時會去判斷是從相對路徑仍是絕對路徑獲取資源。不過api是有一點點坑Class#getResource以'/'開頭與ClassLoader#getResource相同,再記住ClassLoader#getResource不能以'/'開頭就好了。.net
有了前面這個Classr#getResource與ClassLoader#getResource的對比,Class#getResourceAsStream與ClassLoader#getResourceAsStream其實也就ok了。ssr
Class#getResourceAsStream源碼:code
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#getResourceAsStream源碼:接口
public InputStream getResourceAsStream(String name) { URL url = getResource(name); try { return url != null ? url.openStream() : null; } catch (IOException e) { return null; } }
因此,能夠用下面三種方式獲得資源ip
在com.jdk.resource目錄下建立一個test.properties文件,能夠用如下三種方式獲得
package com.jdk.resource; public class Resource { public static void main(String[] args) throws Exception { //第一種方式 System.out.println(Resource.class.getResourceAsStream("test.properties")); //第二種方式 System.out.println(Resource.class.getResourceAsStream("/com/jdk/resource/test.properties")); //第三種方式 System.out.println(Resource.class.getClassLoader().getResourceAsStream("com/jdk/resource/test.properties")); } }
結果:
java.io.BufferedInputStream@659e0bfd java.io.BufferedInputStream@2a139a55 java.io.BufferedInputStream@15db9742
這對查看,Spring Resource 接口的實現ClassPathResource類getInputStream方法有必定幫助
public InputStream getInputStream() throws IOException { InputStream is; if (this.clazz != null) { is = this.clazz.getResourceAsStream(this.path); } else if (this.classLoader != null) { is = this.classLoader.getResourceAsStream(this.path); } else { is = ClassLoader.getSystemResourceAsStream(this.path); } if (is == null) { throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist"); } return is; }