1. 概述
今天在項(xiàng)目中看到下面兩行代碼,看注釋說(shuō)是獲取當(dāng)前工作路徑,之前也沒(méi)有用過(guò)這種用法,比較好奇還能這樣用,所以研究了一下源碼。
//獲取當(dāng)前工作路徑
File file = new File("");
String currentWorkDirectory = file.getAbsolutePath();
2. new File("")解析
首先,new File()是創(chuàng)建一個(gè)虛擬的文件(File)對(duì)象,通過(guò)這個(gè)對(duì)象可以調(diào)用很多方法來(lái)獲取文件和目錄的相關(guān)信息。以下列出一些常用方法:
2.1 File的常用方法
| 方法簽名 | 作用 |
|---|---|
| boolean delete() | 刪除文件或目錄 |
| void deleteOnExit() | 在jvm退出時(shí)刪除文件或目錄 |
| boolean createNewFile() | 當(dāng)以這個(gè)文件名命名的文件不存在時(shí),創(chuàng)建一個(gè)新的空文件 |
| boolean exists() | 判斷文件或目錄是否存在 |
| String getAbsolutePath() | 獲取File對(duì)象的絕對(duì)路徑 |
| String getName() | 獲取文件或目錄名稱(chēng) |
| File getParentFile() | 獲取父File對(duì)象 |
| String getPath() | 獲取File對(duì)象創(chuàng)建時(shí)傳入的pathname參數(shù) |
| boolean isDirectory() | 判斷是否是目錄 |
| File[] listFiles() | 列出當(dāng)前目錄下的所有文件 |
| boolean mkdir() | 創(chuàng)建單個(gè)目錄 |
| boolean mkdirs() | 創(chuàng)建多級(jí)目錄 |
2.2 new File("")做了什么
我們先來(lái)看看源碼是怎么描述的:

其中注釋的意思是:將指定的文件路徑轉(zhuǎn)換為一個(gè)絕對(duì)路徑,然后創(chuàng)建一個(gè)新的File實(shí)例。如果給定的文件路徑參數(shù)為空值,則返回空的絕對(duì)路徑名。
官方的注釋比較難理解,通俗的將就是,當(dāng)我傳入一個(gè)相對(duì)路徑就會(huì)轉(zhuǎn)換為絕對(duì)路徑,當(dāng)我傳入一個(gè)""參數(shù)時(shí),就只有一個(gè)絕對(duì)路徑。那么這個(gè)絕對(duì)路徑代表什么含義呢?
2.3 深入源碼看File絕對(duì)路徑(abstract pathname)的含義
2.3.1 File.getAbsolutePath()方法
其實(shí)我們可以看到new File(String pathname)這個(gè)構(gòu)造方法只設(shè)置了相對(duì)路徑和解析相對(duì)路徑的長(zhǎng)度。并沒(méi)有有關(guān)絕對(duì)路徑的操作。那么究竟是在哪里設(shè)置的絕對(duì)路徑呢?這個(gè)就涉及到getAbsolutePath()方法了。
下面我們看一下getAbsolutePath()方法做了什么:

從注釋可以看出,如果在new File()的時(shí)候傳入的pathname已經(jīng)是絕對(duì)路徑的話(huà),那么這個(gè)方法的返回就和getPath()一樣,如果傳入的是空字符串的話(huà),那么就返回當(dāng)前目錄的路徑,通過(guò)系統(tǒng)屬性u(píng)ser.dir獲取。
2.3.2 WinNTFileSystem.resolve()方法
那么我們繼續(xù)往下看:

點(diǎn)進(jìn)去resolve()方法可以看到返回值實(shí)際是從getUserPath()這個(gè)方法獲取的。

2.3.3 WinNTFileSystem.getUserPath()方法
getUserPath()方法調(diào)用了System.getProperty("user.dir")方法來(lái)獲取user.dir這個(gè)key的配置。再往里面看user.dir的值其實(shí)是從props這個(gè)Properties對(duì)象中獲取的。

2.3.4 System.setProperties(props)方法
那props又是從哪里拿到這個(gè)值的呢?我們接著往下看:
在System類(lèi)里面對(duì)著props這個(gè)屬性用ctrl+鼠標(biāo)左鍵查找props在哪幾個(gè)地方使用了??梢钥吹接幸粋€(gè)setProperties(Properties props)方法。再對(duì)著setProperties這個(gè)方法使用ctrl+鼠標(biāo)左鍵查找。

2.3.5 Process.main()方法
在com.sun.org.apache.xalan.internal.xslt.Process的main方法里面我們可以看到,執(zhí)行了System.setProperties(props),而System.setProperties(props)調(diào)用了本地方法private static native Properties initProperties(Properties props)對(duì)props進(jìn)行初始化。

2.3.6 System.initProperties(Properties props)本地方法

到這里的話(huà),其實(shí)java源代碼已經(jīng)結(jié)束了,總結(jié)來(lái)說(shuō)就是jvm在啟動(dòng)的時(shí)候調(diào)用了System類(lèi)的private static native Properties initProperties(Properties props)本地方法對(duì)props屬性進(jìn)行初始化,設(shè)置了user.dir的值。
這時(shí)可能還有些好奇寶寶不甘心,為什么調(diào)用了這個(gè)本地方法就可以獲取user.dir的值,那么我們就來(lái)研究一下openjdk的c語(yǔ)言源碼吧。
2.4 深入openjdk源碼
我們從github上搜索到openjdk的源碼倉(cāng)庫(kù),隨便找一個(gè)java8的tag來(lái)研究initProperties()這個(gè)本地方法到底做了什么。
- 首先我們找到j(luò)dk/src/share/native/java/lang目錄下面的System.c文件。在里面搜索一下initProperties,可以看到有一個(gè)Java_java_lang_System_initProperties方法。

- 接著我們?cè)谶@個(gè)方法里面搜索user_dir,可以看到值是從一個(gè)sprops指針中獲取的,而sprops從上一張圖可以看出是從GetJavaProperties(env)這個(gè)方法獲取的值。

- 那我們ctrl+shift+F全局搜索一下GetJavaProperties這個(gè)方法。可以看到j(luò)dk/src/windows/native/java/lang/java_props_md.c文件中有這個(gè)方法,從文件路徑可以看出這個(gè)文件是windows系統(tǒng)下執(zhí)行的。

- 接著在這個(gè)方法里面搜索user_dir,可以看到user_dir實(shí)際是從GetCurrentDirectoryW(sizeof(buf)/sizeof(WCHAR), buf)方法里面獲取值的。GetCurrentDirectoryW這個(gè)方法就是windows的系統(tǒng)函數(shù),用來(lái)獲取當(dāng)前工作目錄。

到這里jdk源碼解析就結(jié)束了,可以看到在windows系統(tǒng)下是調(diào)用了它的系統(tǒng)函數(shù)獲取當(dāng)前工作目錄。
備注:_wcsdup(buf)方法是c語(yǔ)言的庫(kù)函數(shù),作用是分配一塊新的空間,然后從buf數(shù)組里面獲取值賦值給新創(chuàng)建的空間。
3. 總結(jié)
- 絕對(duì)路徑是什么:當(dāng)前工作目錄,就是當(dāng)前項(xiàng)目的根路徑
- 使用new File("")創(chuàng)建File對(duì)象時(shí),絕對(duì)路徑其實(shí)是getAbsolutePath()方法獲取的,new File("")只是設(shè)置了path=""
- System.props這個(gè)屬性其實(shí)包含了很多值,如:java.version、user.home等等,可以看源碼中的注釋
- 通過(guò)System.getProperty(key)可以獲取系統(tǒng)的一些配置信息