1. 程式人生 > >JAVA獲取路徑問題

JAVA獲取路徑問題

下午在公司納涼,何同學突然問我,他的hadoop 有個java類如何獲取在根目錄的文字檔案。

目錄是這個樣子的。

JavaPath 類 要讀取資料夾testfolder的test.txt 文字。

寫成絕對路徑問題就解決了,也就是寫成D://workspace/JavaPath/testfolder/test.txt 可是這個這個程式要打成jar包,放到伺服器上執行,這時絕對路徑就不靈了。

問題來了,就得想辦法解決!

之前也沒一直深入研究java路徑的獲取問題,不會了,咱們就百度唄,前輩們可定遇見過這樣的問題。果然第一篇檔案就是對java獲取路徑的總結:

1、JavaPath.class.getResource("") //得到的是當前類FileTest.class檔案的URI目錄。不包括自己!

結果:file:/D:/workspace/JavaPath/bin/cn/com/umesage/path/

2、JavaPath.class.getResource("/")得到的是當前的classpath的絕對URI路徑。

結果:file:/D:/workspace/JavaPath/bin/

咱們來看看Class類的getResource 方法原始碼

public java.net.URL getResource(String name) {
        name = resolveName(name);//1
        ClassLoader cl = getClassLoader0();//2
        if (cl==null) {//3
            // A system class.
            return ClassLoader.getSystemResource(name);//4
        }
        return cl.getResource(name);//5
    }


一共 5行有效程式碼,咱們進入第一行,看看resoveName是如何實現的,廢話不說上程式碼
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;
    }


這個方法邏輯是 如果輸入的是“/” 那麼就變成 “”  也就是 else 裡面的程式碼, 如何輸入的是“”  則if 為真 進入裡面執行也就是把cn.com.umesage.path.JavaPath 中的點變成能讓檔案系統識別的cn/com/umesage/path/

接著看看getResource 方法的第二段程式碼,很遺憾是native 方法

// Package-private to allow ClassLoader access
    native ClassLoader getClassLoader0();


但是邏輯應該能看懂,意思是獲取classloader,話外補充一下 類的載入器 ,此文不會深入討論,只是初步介紹。
Java 中的類載入器大致可以分成兩類,一類是系統提供的,另外一類則是由 Java 應用開發人員編寫的。系統提供的類載入器主要有下面三個:
  • 引導類載入器(bootstrap class loader):它用來載入 Java 的核心庫,是用原生程式碼來實現的,並不繼承自 java.lang.ClassLoader
  • 擴充套件類載入器(extensions class loader):它用來載入 Java 的擴充套件庫。Java 虛擬機器的實現會提供一個擴充套件庫目錄。該類載入器在此目錄裡面查詢並載入 Java 類。
  • 系統類載入器(system class loader):它根據 Java 應用的類路徑(CLASSPATH)來載入 Java 類。一般來說,Java 應用的類都是由它來完成載入的。可以通過ClassLoader.getSystemClassLoader()來獲取它。

下面是一個示例,讓大家瞭解一下這三種類載入器的使用場景

public class JavaClassLoader {

	public static void main(String[] args) {
		System.out.println(String.class.getClassLoader());// 引導類載入器
		ClassLoader loader = JavaPath.class.getClassLoader();
		while (loader != null) {
			System.out.println(loader.toString()); // 系統類載入器
			loader = loader.getParent(); // 擴充套件類載入器 ;JavaClassLoader的父類是Object類
		}

	}




上圖是執行的結果

還是說了太多 和本篇無關的話.....

由之前的類載入器的介紹,就能知道getResource 方法中的第二行返回的不是null,因為它是由系統載入器載入的,所以跳到第五行。

第五行是return cl.getResource(name); 那麼咱們進入 ClassLoader 的getResource 方法 原始碼如下:

 // -- Resource --

    /**
     * Finds the resource with the given name.  A resource is some data
     * (images, audio, text, etc) that can be accessed by class code in a way
     * that is independent of the location of the code.
     *
     * <p> The name of a resource is a '<tt>/</tt>'-separated path name that
     * identifies the resource.
     *
     * <p> This method will first search the parent class loader for the
     * resource; if the parent is <tt>null</tt> the path of the class loader
     * built-in to the virtual machine is searched.  That failing, this method
     * will invoke {@link #findResource(String)} to find the resource.  </p>
     *
     * @param  name
     *         The resource name
     *
     * @return  A <tt>URL</tt> object for reading the resource, or
     *          <tt>null</tt> if the resource could not be found or the invoker
     *          doesn't have adequate  privileges to get the resource.
     *
     * @since  1.1
     */   
public URL getResource(String name) {
	URL url;
	 if (parent != null) {
	     url = parent.getResource(name);
 	} else {
 	    url = getBootstrapResource(name);
 	}
 	if (url == null) {
	     url = findResource(name);
 	}
 	return url;
    }


這段程式碼的註釋 已解釋的很清楚,就不再解釋了,最後跟下去全是native 方式,有興趣 可以繼續研究JNI是如何實現的。

3.Thread.currentThread().getContextClassLoader().getResource("") //得到的也是當前ClassPath的絕對URI路徑。

結果:file:/D:/workspace/JavaPath/bin/

4 JavaPath.class.getClassLoader().getResource("") //得到的也是當前ClassPath的絕對URI路徑。

結果:file:/D:/workspace/JavaPath/bin/
5 ClassLoader.getSystemResource("")//得到的也是當前ClassPath的絕對URI路徑。

結果:file:/D:/workspace/JavaPath/bin/
 對於何同學提出的檔案相對路徑 還是沒有得到解決。

那麼咱麼就接著想辦法。能不能通過絕對路徑拼出個相對路徑來

還好FIle 類 給咱們提供的獲取當前路徑的方法

File directory = new File("");//設定為當前資料夾// . 當前目錄 ..是父目錄,實現的程式碼如下

public class JavaPath {

	private final static String TEXT_URL = new java.io.File("").getAbsolutePath()+"/testfolder/test.txt";
	
	public static void main(String[] args) throws IOException {
		FileInputStream fis = new FileInputStream(new File(JavaPath.TEXT_URL));
		int word;
		while ((word=fis.read())!=-1) {
			System.out.print((char)word);
		}
		fis.close();
		
	}

}



問題總算解決了,希望可以幫助何同學執行起來。

關於FIle 類, 將在下篇文章詳細介紹。