您好,登錄后才能下訂單哦!
本篇內容介紹了“Java怎么使用類路徑獲取資源”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
Class 的 getResource 方法
ClassLoader 的 getResource 方法
ClassLoader 的 getSystemResource 靜態方法
在使用中,Class 可通過直接引用類的 class 屬性而獲得,或是通過實例的 getClass() 方法來獲得。獲取 ClassLoader 的方式則比較多,常見以下幾種:
調用 Class 的 getClassLoader 方法,如:getClass().getClassLoader()
由當前線程獲取 ClassLoader:Thread.currentThread().getContextClassLoader()
獲取系統 ClassLoader: ClassLoader.getSystemClassLoader()
不過,若是對 Java 的 ClassLoader 概念不太了解,最好還是盡量避免使用它。
這兩種方式,都接受一個字符串形式的路徑表達式,即資源名,并返回找到的資源的 URL。兩種方式都可用來定位資源,在網絡上流傳的文章中,兩者都是常見的。實際上,Class 的 getResource 方法也調用了 ClassLoader 的 getResource 方法,但兩者有著很大的不同,不了解這兩種方法的區別,就容易造成隱患。隱患經常比編寫時就出錯要可怕得多,因為它在一定場合下是正常的,不容易被發現。
兩者最大的區別,是從哪里開始尋找資源。ClassLoader 并不關心當前類的包名路徑,它永遠以 classpath 為基點來定位資源。而 Class.getResource 則不同,如果資源名是絕對路徑(以"/"開頭),它會將開頭的"/"去除,然后調用 ClassLoader 的 getResource 方法來尋找資源;如果資源名是相對路徑,它會在當前的包路徑下面尋找資源。
舉例來說,假設我們有一個類:test.App (包名為 test),并且在 test 包下有一個與類名同名的 js 文件,名為 App.js。如果用 ClassLoader 來獲取這個 js 文件,應該這樣寫:
App.class.getClassLoader().getResource("test/App.js");
如果用 Class 的 getResource 方法,則有兩種寫法:
使用相對路徑:
App.class.getResource("App.js");
使用絕對路徑:
App.class.getResource("/test/App.js");
從上面的例子,可以看出兩者之間巨大的區別。有些人從網絡上復制類似的代碼,看看不能正確運行,就開始嘗試在資源名前加上 "/",或是去掉開頭的 "/",試成功了,便算完工,這絕非正道。
Class 與 ClassLoader 的 getResource 方法還有其它一些不同,對 Class 的 getResource 方法來說,若傳入的是相對路徑,它還會嘗試做包名與路徑名的轉換。查看 Class.getResource 方法的源碼,可以看到它首先對資源名調用了 resolveName 方法,然后再調用 ClassLoader 的 getResource 方法來完成資源的定位。
作為演示,我寫了以下代碼來展示 Class 與 ClassLoader 的 getResource 方法的輸出:
/**
* Copyright (c) 2014 Chen Zhiqiang <chenzhiqiang@mail.com>. Released under the MIT license.
*/
package test;
import java.net.URL;
import java.util.Enumeration;
/**
* Tests for the use of {@link Class#getResource(String)} and
* {@link ClassLoader#getResource(String)}.
*
* @author Chen Zhiqiang <chenzhiqiang@mail.com>
*/
public class ClassResourceTest {
Class<ClassResourceTest> cls = ClassResourceTest.class;
ClassLoader ldr = cls.getClassLoader(); // Thread.currentThread().getContextClassLoader()
public static void println(Object s) {
System.out.println(s);
}
void showResource(String name) {
println("## Test resource for: “" + name + "” ##");
println(String.format("ClassLoader#getResource("%s")=%s", name, ldr.getResource(name)));
println(String.format("Class#getResource("%s")=%s", name, cls.getResource(name)));
}
public final void testForResource() throws Exception {
showResource("");
showResource("/");
showResource(cls.getSimpleName() + ".class");
String n = cls.getName().replace('.', '/') + ".class";
showResource(n);
showResource("/" + n);
showResource("java/lang/Object.class");
showResource("/java/lang/Object.class");
}
public static void main(String[] args) throws Exception {
println("java.class.path: " + System.getProperty("java.class.path"));
println("user.dir: " + System.getProperty("user.dir"));
println("");
ClassResourceTest t = new ClassResourceTest();
t.testForResource();
}
}
編譯上述代碼,看看不同資源路徑的輸出結果。
現在,將上述代碼編譯后的結果打包成 Jar 文件,假設是 test.jar ,然后從這個 jar 包中運行上述代碼,再看看輸出結果,比較下與上面的輸出有什么變化:
java -classpath test.jar test.ClassResourceTest
值得注意的幾點:
Class.getResource("") 還有其它一些輸出,結果是 jar:file:/some_path/test.jar!/some_path,而在打包為 Jar 之前,它們的輸出形式是 file:/some_path...;
Class.getResource("/") 為 null,而在打包之前,該輸出是 ClassResourceTest 的類路徑;
ClassLoader.getResource("") 為 null,而在打包之前,該輸出是 ClassResourceTest 的類路徑;
調用 ClassLoader.getResource 方法時,若資源名為絕對路徑,不管是否打包,其輸出結果為 null,至少在我這里是這樣。
使用 Class.getResource("/") 或 ClassLoader.getResource("") 來當作類路徑的根。
這是一種常見的錯誤,并在網絡上廣為流傳。它們在打包成 Jar 包后,其結果會發生變化。
獲得 getResource 方法的輸出后,簡單地對結果調用 getFile 或 getPath,并把它當作文件路徑來處理。
資源有可能以文件和目錄的形式位于類路徑之中,但也可能打包進了 Jar 包或 Zip 包,你不能假設你的代碼不會被打包。
將絕對路徑傳給 ClassLoader 的 getResource 方法。
網絡上有人說,對于 ClassLoader 的 getResource 方法來說,資源名是否以 "/" 開頭是一樣的,然而,在我這里,ClassLoader 的 getResource 方法并不接受絕對路徑,其輸出結果為 null。
避免使用 Class.getResource("/") 或 ClassLoader.getResource("")。你應該傳入一個確切的資源名,然后對輸出結果作計算。比如,如果你確實想獲取當前類是從哪個類路徑起點上執行的,以前面提到的 test.App 來說,可以調用 App.class.getResource(App.class.getSimpleName() + ".class")。如果所得結果不是 jar 協議的URL,說明 class 文件沒有打包,將所得結果去除尾部的 "test/App.class",即可獲得 test.App 的類路徑的起點;如果結果是 jar 協議的 URL,去除尾部的 "!/test/App.class",和前面的 "jar:",即是 test.App 所在的 jar 文件的 url。
如果要定位與某個類同一個包的資源,盡量使用那個類的getResource方法并使用相對路徑。如前文所述,要獲取與 test.App.class 同一個包下的 App.js 文件,應使用 App.class.getResource("App.js") 。當然,事無絕對,用 ClassLoader.getResource("test/App.js") 也可以,這取決于你所面對的問題是什么。
如果對 ClassLoader 不太了解,那就盡量使用 Class 的 getResource 方法。
如果不理解或無法確定該傳給 Class.getResource 方法的相對路徑,那就以類路徑的頂層包路徑為參考起點,總是傳給它以 "/" 開頭的路徑吧。
不要假設你的調試環境就是最后的運行環境。你的代碼可能不打包,也可能打包,你得考慮這些情況,不要埋坑。
Java 的 CLASSPATH 是一個路徑列表,因此,有可能在多個類路徑中出現同樣的資源名。如果要列舉它們,可以使用 ClassLoader 的 getResources 方法。
下面的代碼可以枚舉所有的 "META-INF/MANIFEST.MF",你還可以觀察到在類路徑中哪些 jar 文件包含有該資源:
import java.net.URL;
import java.util.Enumeration;
public class Test {
public static void main(String[] args) throws Exception {
ClassLoader ldr = Test.class.getClassLoader();
System.out.println("## Test for getResources(‘META-INF/MANIFEST.MF') ##");
Enumeration<URL> urls = ldr.getResources("META-INF/MANIFEST.MF");
while(urls.hasMoreElements())
System.out.println(urls.nextElement());
}
}
下面的代碼演示了如何正確獲取代碼的類路徑起點:
/**
* Copyright (c) 2014 Chen Zhiqiang <chenzhiqiang@mail.com>. Released under the MIT license.
*/
package test;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 演示如何獲取當前類路徑的起點
*
* @author Chen Zhiqiang <chenzhiqiang@mail.com>
*/
public class AppDirTest {
Classcls = AppDirTest.class;
URL codeLocation = getCodeLocation();
/**
* Get the code location.
*
* Return the classpath where the code run from. The return url will be:
* file:/path/my-app/calsses/ or file:/path/my-app/my-app.jar
*
* @return URL
*/
public URL getCodeLocation() {
if (codeLocation != null)
return codeLocation;
// Get code location using the CodeSource
codeLocation = cls.getProtectionDomain().getCodeSource().getLocation();
if (codeLocation != null)
return codeLocation;
// If CodeSource didn't work, use {@link } Class.getResource instead.
URL r = cls.getResource("");
synchronized (r) {
String s = r.toString();
Pattern jar_re = Pattern.compile("jar:\s?(.*)!/.*");
Matcher m = jar_re.matcher(s);
if (m.find()) { // the code is run from a jar file.
s = m.group(1);
} else {
String p = cls.getPackage().getName().replace('.', '/');
s = s.substring(0, s.lastIndexOf(p));
}
try {
codeLocation = new URL(s);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
return codeLocation;
}
/**
* Get the class path root where the program startup, if run in a jar,
* return the jar file's parent path.
*
* @return
*/
public String getAppDir() {
File f = new File(getCodeLocation().getPath());
return f.isFile() ? f.getParent() : f.getPath();
}
public static void main(String[] args) {
AppDirTest t = new AppDirTest();
System.out.println("code location: " + t.getCodeLocation());
System.out.println("app dir: " + t.getAppDir());
}
}
“Java怎么使用類路徑獲取資源”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。