Java 中獲取資源(文件)的路徑問題總結

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方式獲取資源.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章