Java 中獲取資源(文件)的路徑問題總結
首先,Java 中獲取資源大體上可分爲兩種方式,基於 文件系統的 和 基於classpath的.
1. 基於文件系統的相對簡單.
比如 構造一個File f = new File('text.txt');
這裏傳遞給File構造函數的可以是相對路徑比如text.txt就是相對路徑,
也可以是絕對路徑比如 new File('C:/text.txt');
需要注意的是,這裏相對路徑,相對的是System.getProperties("user.dir")的
比如 你用window中的cmd 通過調用java命令來來執行一個java程序,
那麼,cmd中的當前路徑,就是Java程序中的相對路徑
比如 C:\> java test 執行這條命令,相對路徑就在C盤.
2. 基於classpath的
我們知道,java 命令執行的時候可以指定一個classpath,系統默認在這個classpath目錄
下面查找各種calss.文件,jar包,配置文件等.
基於classpath 獲取資源有以下三種方式:
URL url = this.getClass().getResource("resource_name");
URL url = this.getClass().getClassLoader().getResource("resource_name");
URL url = Thread.currentThread().getContextClassLoader().getResource("resource_name");
第一種是通過Class類實例的getResource方法,後面兩種都是ClassLoader類實例的getResource方法.
Class.getResource()也是委託ClassLoader的getResource方法來實現的.
所以,先說ClassLoader的getResource方法:
(1) ClassLoader的getResource方法參數不能以"/"開頭,而且必須是從根目錄開始查找,
這裏的根目錄是classpath中的目錄以及包含引用的jar.
比如eclipse的默認將每個工程中Java類運行時的classpath設置爲:
工程根目錄/bin目錄 以及 工程中引用的所有jar包.
在編譯的時候,將src目錄結構拷貝到bin目錄中,將java類編譯成class文件後連同其他文件按src中原始目錄結構
拷貝到bin目錄中.
假設某個工廠的classpath如下(兩個):
/bin
log4j-1.2.16.jar
其中log4j-1.2.16.jar中有目錄結構org\apache\log4j\ (與包org.apach.log4j) 對應
那麼 查找bin目錄下的test.txt文件 使用下面方法
ClassLoader.getResource("test.txt");
注意這裏ClassLoader.getResource方法的入參必須是從根目錄開始查找,這裏根目錄就是classpath中的/bin.
找 bin/level1/level2/ll.txt文件必須使用
ClassLoader.getResource("level1/level2/ll.txt"); //注意查找必須基於根目錄(/bin),並且目錄結構也要寫對,不能用/開頭
(2) Class.getResource() 略有不同:
(a)可以通過相對路徑查找,相對的是 當前實例的Class文件所在的包;
(b)也可以和ClassLoader.getResource()一樣從根目錄(classpath)開始查找,
但是此時傳遞給Class.getResource()的參數必須要用 "/" 開頭,
否則就是相對查找了((a)中的情況)
其實,這種代碼就是將/去掉,然後調用ClassLoader.getResource()
參考代碼:
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);
}
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;
}
需要注意的是,這種基於classpath查找的情況,在寫代碼之前需要把各種系統的classpath研究清楚.
比如Tomcat的不同版本classpath的設置不同,需要了解清楚(參考:http://my.oschina.net/dongming/blog/64142)
關於 getClassLoader().getResource 和 Thread.currentThread().getContextClassLoader().getResource區別:
因爲類似Tomcat這類的容器,可能使用了自定義的ClassLoader產生了特殊的classpath,這樣就需要遵循特殊的方式,
Thread.currentThread().getContextClassLoader()返回該線程的上下文 ClassLoader,再調用getResource更保險
一些,一般推薦使用Thread.currentThread().getContextClassLoader().getResource方式獲取資源.