1. 程式人生 > 實用技巧 >Java IO: 讀取classpath資源

Java IO: 讀取classpath資源

classpath

classpath是JVM用到的一個環境變數,它用來指示JVM如何搜尋class。因為Java是編譯型語言,原始碼檔案是.java,而編譯後的.class檔案才是真正可以被JVM執行的位元組碼。因此,JVM需要知道,如果要載入一個abc.xyz.Hello的類,應該去哪搜尋對應的Hello.class檔案。所以,classpath就是一組目錄的集合,它設定的搜尋路徑與作業系統相關。例如,在Windows系統上,用;分隔,帶空格的目錄用""括起來,可能長這樣:

C:\work\project1\bin;C:\shared;"D:\My Documents\project1\bin"

在Linux系統上,用:分隔,可能長這樣:

/usr/shared:/usr/local/bin:/home/liaoxuefeng/bin

現在我們假設classpath.;C:\work\project1\bin;C:\shared,當JVM在載入abc.xyz.Hello這個類時,會依次查詢:

  • <當前目錄>\abc\xyz\Hello.class
  • C:\work\project1\bin\abc\xyz\Hello.class
  • C:\shared\abc\xyz\Hello.class

注意到.代表當前目錄。如果JVM在某個路徑下找到了對應的class檔案,就不再往後繼續搜尋。如果所有路徑下都沒有找到,就報錯。

classpath的設定方法

classpath的設定方法有兩種:

  1. 在系統環境變數中設定classpath環境變數,不推薦;

  2. 在啟動JVM時設定classpath變數,推薦。

強烈不推薦在系統環境變數中設定classpath,那樣會汙染整個系統環境。在啟動JVM時設定classpath才是推薦的做法。實際上就是給java命令傳入-classpath-cp引數:

java -classpath .;C:\work\project1\bin;C:\shared abc.xyz.Hello

或者使用-cp的簡寫:

java -cp .;C:\work\project1\bin;C:\shared abc.xyz.Hello

沒有設定系統環境變數,也沒有傳入-cp引數,那麼JVM預設的classpath.,即當前目錄:

java abc.xyz.Hello

上述命令告訴JVM只在當前目錄搜尋Hello.class

在IDE中執行Java程式,IDE自動傳入的-cp引數是當前工程的bin目錄和引入的jar包。通常,我們在自己編寫的class中,會引用Java核心庫的class,例如,StringArrayList等。這些class應該上哪去找?不需要告訴JVM如何去Java核心庫查詢class,JVM可以自主找到自己的核心庫。 不要把任何Java核心庫新增到classpath中!JVM根本不依賴classpath載入核心庫!更好的做法是,不要設定classpath!預設的當前目錄.對於絕大多數情況都夠用了。

從classpath讀取檔案

很多Java程式啟動的時候,都需要讀取配置檔案。例如,從一個.properties檔案中讀取配置:

String conf = "C:\\conf\\default.properties";
try (InputStream input = new FileInputStream(conf)) {
    // TODO:
}

這段程式碼要正常執行,必須在C盤建立conf目錄,然後在目錄裡建立default.properties檔案。但是,在Linux系統上,路徑和Windows的又不一樣。因此,從磁碟的固定目錄讀取配置檔案,不是一個好的辦法。

有沒有路徑無關的讀取檔案的方式呢?

從classpath讀取檔案就可以避免不同環境下檔案路徑不一致的問題:如果我們把default.properties檔案放到classpath中,就不用關心它的實際存放路徑。

在classpath中的資原始檔,路徑總是以開頭,我們先獲取當前的Class物件,然後呼叫getResourceAsStream() 就可以直接從classpath讀取任意的資原始檔:

try (InputStream input = getClass().getResourceAsStream("/default.properties")) {
    // TODO:
}

呼叫getResourceAsStream()需要特別注意的一點是,如果資原始檔不存在,它將返回null。因此,我們需要檢查返回的InputStream是否為null,如果為null,表示資原始檔在classpath中沒有找到:

try (InputStream input = getClass().getResourceAsStream("/default.properties")) {
    if (input != null) {
        // TODO:
    }
}

如果我們把預設的配置放到jar包中,再從外部檔案系統讀取一個可選的配置檔案,就可以做到既有預設的配置檔案,又可以讓使用者自己修改配置:

Properties props = new Properties();
props.load(inputStreamFromClassPath("/default.properties"));
props.load(inputStreamFromFile("./conf.properties"));

這樣讀取配置檔案,應用程式啟動就更加靈活。

小結

把資源儲存在classpath中可以避免檔案路徑依賴;Class物件的getResourceAsStream()可以從classpath中讀取指定資源;

根據classpath讀取資源時,需要檢查返回的InputStream是否為null