使用Java很多年了,雖然對如何搜索classes有一定的認識,但精通卻不敢妄說。尤其是最近在使用getResourceAsStream加載屬性文件時,遇到了不大不小的幾個問題,當然遇到的問題與不熟悉Class.getResourceAsStream和ClassLoader. getResourceAsStream有直接關系,但多少與沒有徹底理解Java中搜索classes的原理有關,所以在總結Class.getResourceAsStream和ClassLoader. getResourceAsStream的用法和區別之前,先學習一下Java是如何搜索classes的。
在Java中存在三種類型的class,分別是:啟動程序classes(Bootstrapclasses)、擴展classes(Extension classes)和用戶自定義類(即程序員實現的各種類)。啟動程序classes組成了Java平台,包括rt.jar和其它jar包中的類。擴展classes使用了Java的擴展機制,位於jre/lib/ext目錄中。用戶classes由開發人員實現,在命令行使用-classpath選項或者設置CLASSPATH環境變量標識用戶classes的位置。
當在命令行使用java命令執行class文件時,該命令啟動Java虛擬機,虛擬機按照啟動程序classes、擴展classes和用戶classes的順序搜索和加載類,這三種路徑組成了類路徑。在現如今的版本中,啟動程序classes和擴展classes會被Java虛擬機自動發現和加載,只需要指定用戶classes的位置,即使用-classpath或者設置CLASSPATH環境變量,工具類位於tools.jar中,要想使用工具類必須在classpath中包含tools.jar。
查找啟動程序classes
可以System.getProperty("sun.boot.class.path")方法查看包含classes的jar文件,在本機上該方法的輸出如下:
D:\software\Java\jre6\lib\resources.jar;D:\software\Java\jre6\lib\rt.jar;D:\software\Java\jre6\lib\sunrsasign.jar;D:\software\Java\jre6\lib\jsse.jar;D:\software\Java\jre6\lib\jce.jar;D:\software\Java\jre6\lib\charsets.jar;D:\software\Java\jre6\lib\modules\jdk.boot.jar;D:\software\Java\jre6\classes
幾乎不會出現需要重新定義啟動程序類路徑的情況,不過還是提供了非標准選項 –Xbootclasspath允許設置不同的啟動程序類路徑。
查找擴展classes
擴展classes擴展了Java平台,位於jre/lib/ext目錄中。需要注意的是擴展classes必須被打包為jar或者zip文件,否則不會被查找到。不像啟動程序classes,沒有選項用於更改擴展classes的目錄。如果jre/lib/ext目錄包含多個jar文件,並且這些jar文件包含相同名稱的類,如:
smart-extension1_0.jar contains class smart.extension.Smart
smart-extension1_1.jar contains class smart.extension.Smart
那么實際被加載的類是未定義的。
查找用戶classes
用戶classes是建立在Java平台基礎上的類,為了查找用戶classes,Java啟動器引用用戶的classpath,即包含類文件的目錄、jar文件和zip文件的列表。類文件有一個反射類全限定名稱的子路徑名稱,例如如果類com.mypackage.MyClass保存在/myclasses,那么/myclasses必須在用戶類路徑中,類文件的完整路徑為/myclasses/com/mypackage/MyClass.class。如果類文件保存在名為myClasses.jar的jar文件中,那么該jar文件必須位於類路徑中,並且類文件必須以com/mypackage/MyClass.class保存在jar文件中。
用戶的類路徑為一個字符串,在Linux系統中使用冒號(:)分隔不同的項,在Windows上使用分號(;)分隔不同的項。Java啟動器將類路徑字符串放入java.class.path 系統屬性中,可以使用System.getProperty("java.class.path")查看該屬性的值。該屬性的可能的值為:
默認值.,意味着用戶類文件為當前目錄下的所有類文件或者該目錄下包中的類文件。
CLASSPATH環境變量的值,該值將會覆蓋默認值。
-cp或者-classpath命令行選項指定的值,該值將覆蓋默認值和CLASSPATH的值。
-jar選項執行的jar文件,該值將覆蓋掉所有其它的值,即默認值,CLASSPATH的值和-cp或-classpath指定的值。如果使用了-jar選項,所有的用戶類文件必須為jar文件中的類文件。
對於最后一條規則,可以通過實際情況驗證一下,在bin目錄包含屬性文件test.properties。將測試代碼打包為test-classpath.jar,然后在命令行運行java –jartest-classpath.jar,結果如下:
D:\BigData\TestClassPath\bin>java -jar test-classpath.jar
Class.getResourceAsStream can not find resource on classpath
ClassLoader.getResourceAsStreamcan not find resource on classpath
而直接運行java test.TestGetResourceAsStream的結果如下:
D:\BigData\TestClassPath\bin>java test.TestGetResourceAsStream
Class.getResourceAsStream can not find resource on classpath
ClassLoader.getResourceAsStream:Hello world!
由結果驗證了當使用-jar選項時,將會覆蓋所有其它的值。