我們知道不同的作業系統有各自的檔案系統,這些檔案系統又存在很多差異,而Java 因為是跨平台的,是以它必須要統一處理這些不同平台檔案系統之間的差異,才能往上提供統一的入口。
JDK 裡面抽象出了一個 FileSystem 來表示檔案系統,不同的作業系統通過繼承該類實作各自的檔案系統,比如 Windows NT/2000 作業系統則為 WinNTFileSystem,而 unix-like 作業系統為 UnixFileSystem。
需要注意的一點是,WinNTFileSystem類 和 UnixFileSystem類并不是在同一個 JDK 裡面,也就是說它們是分開的,你隻能在 Windows 版本的 JDK 中找到 WinNTFileSystem,而在 Linux 版本的 JDK 中找到 UnixFileSystem,同樣地,其他作業系統也有自己的檔案系統實作類。
這裡分成兩個系列分析 JDK 對兩種(Windows 和Linux)作業系統的檔案系統的實作類,先講 Windows作業系統,對應為 WinNTFileSystem 類。 由于篇幅較長,《JDK不同作業系統的FileSystem(Windows)》分為上中下篇,此為上篇。
slash 表示斜杠符号。
altSlash 與slash相反的斜杠。
semicolon 表示分号。
driveDirCache 表示驅動盤目錄緩存。
cache 用于緩存标準路徑。
prefixCache 用于緩存标準路徑字首。
構造方法很簡單,先通過 System.getProperties() 擷取 Properties 對象,然後擷取其裡面的 file.separator 屬性和 path.separator 屬性的值, 分别指派給相應變量,在 Windows 中這兩個值分别為 <code>\</code> 和 ; 。最後将斜杠 <code>/</code> 賦給 altSlash。
判斷是不是斜杠。
判斷是不是字母。
判斷一個字元串是否以斜杠開頭,不是則幫其開頭添加斜杠,是則不作處理。
該方法主要是對路徑進行标準化,它在實作過程中依賴另外一個 normalize 方法和 normalizePrefix 方法,這兩個方法都是 private 的。
針對傳入來的 path 變量,用一個 for 循環周遊每個字元,分别對以下三種情況處理,
1. 當遇到 altSlash 時,即 <code>/</code> 時,則把 path 傳入另外一個 normalize 方法中進行處理,其中涉及 prev == slash 判斷條件,prev 其實就是前一個字元,相等就說明兩個 <code>/</code> 連着。
2. 當遇到連續兩個 slash 時,即連續兩個 <code>\</code> 時,而且 i 還要大于1時,則把 path 傳入另外一個 normalize 方法中進行處理。
3. 當遇到 : 字元且 i 大于1時,則把 path 傳入另外一個 normalize 方法中進行處理。
如果都不在上述情況内,則要繼續判斷最後一個字元是否為 slash ,如果是則還要傳入另外一個 normalize 方法中進行處理。否則直接傳回 path ,這時其實 path 就是以一個或兩個 <code>\</code> 開頭且後面不再存在斜杠或反斜杠或冒号,這種情況是可以直接傳回的。
往下看具體的處理邏輯,這裡有三個參數,第一個是路徑字元串,第二個是路徑長度,第三個是路徑字元串的偏移,偏移量用來表示從哪個位置開始,偏移量 off 不能小于3,這是考慮到了UNC路徑。繼續往下如果偏移量等于0的話則先處理字首,這時調用 normalizePrefix 方法處理。偏移量非0的情況下則表示已經有部分已經标準化好了,将其先 append 到 StringBuilder 對象中。
接着開始處理從偏移量開始到結尾的路徑,用 while 循環周遊剩餘路徑中的每個字元,如果有連着都是斜杠的情況則跳過重複的斜杠,這裡斜杠包括了 <code>/</code> 和 <code>\</code> 。非斜杠的情況則直接将字元 append 到 StringBuilder 對象中,多個斜杠則隻添加一個斜杠。最後 src == len 條件則表示已經到結尾了,這時要考慮一些特殊情況的處理,比如 <code>c:\\</code> 、 <code>\\</code> 和 <code>\\\\</code>。
正常情況下,Windows的路徑不會存在連着的兩個斜杠(除了UNC路徑可能會兩個斜杠開頭),同時也不會以斜杠結束。路徑一般分為:目錄相對路徑、驅動盤相對路徑、UNC絕對路徑和本地絕對路徑。以下兩種邏輯分别處理類似<code>c:</code>和<code>\\</code>。
綜上處理邏輯,為幫助我們更好地了解,用以下不同路徑格式看看對應的标準化後是什麼樣的。
該方法主要是擷取路徑字首的長度。按照順序看下邏輯,擷取第一個第二個字元,如果都為 slash ,即兩個<code>\</code>,則為 UNC 路徑,形如 <code>\\test</code>,傳回2;如果第二個字元不是<code>\</code>則為驅動盤相對路徑,形如<code>\test</code>,傳回1;當第一個字元為字母且第二個為<code>:</code>時,如果第三個字元為<code>\</code>,則為本地絕對路徑,形如<code>c:\test</code>,傳回3;如果第三個字元為非<code>\</code>,則為目錄相對路徑,形如<code>c:test</code>;最後則為相對路徑,形如<code>test</code>。
通過 System 擷取 user.dir 屬性作為使用者路徑。
擷取驅動盤,先擷取路徑頭部長度,再截取驅動盤。
擷取驅動盤的索引值,按照字母順序,比如 a 或 A 則索引值為0。
擷取指定驅動盤下的工作目錄,每個驅動盤都有工作目錄。可以看到有兩個 getDriveDirectory 方法,其中一個本地方法,實作需要本地方法來支援。其中邏輯是先根據驅動盤擷取對應的驅動盤索引,然後再将索引加一并通過本地方法擷取對應驅動盤目前工作目錄,這裡還會将其緩存起來,友善後面查詢。
本地的實作如下,主要看函數 currentDir,先通過作業系統的API函數 GetDriveTypeW 判斷是否為不合格的驅動盤類型,這其中參數都是用寬字元。接着通過 _wgetdcwd 函數擷取指定驅動器上的目前工作目錄的完整路徑,同時去掉驅動盤和冒号,傳回給 Java 層一個表示目前工作目錄路徑的字元串。
有兩個resolve方法。
第一個 resolve 方法主要是針對傳入的兩個參數,一個是父路徑一個是子路徑,對它們進行解析然後得到一個新路徑。此過程需要考慮兩個路徑的格式。邏輯如下:
1. 先分别擷取父路徑長度和子路徑長度。
2. 根據父路徑判斷是否為目錄相對路徑,形如<code>c:</code>的。
3. 若子路徑以 slash 即<code>\</code>開頭,則可能是 UNC 路徑,這時要丢棄它的頭部,是以子路徑從第2的位置開始;也可能是驅動盤相對路徑,這時丢棄它的頭部,子路徑從第1的位置開始;最後如果子路徑為兩個 slash 即 <code>\\</code>時,則直接傳回父路徑,當然父路徑如果以 slash 結尾也要将其去掉。
4. 此時确定好了父路徑的長度、父路徑的結束位置、子路徑的長度和子路徑的開始位置,就可以得到最終的新路徑的長度了。
5. 根據上述的長度和位置資訊将父路徑和子路徑合并,傳回一個新的路徑。
第二個 resolve 方法傳入的是 File,主要是根據 File 對應的不同類型路徑解析處理然後傳回。
1. 擷取路徑頭部。
2. 如果頭部長為2且以<code>\</code>開頭,此時為 UNC 路徑,直接傳回路徑。
3. 如果頭部長為3,則為本地絕對路徑,直接傳回路徑。
4. 如果長度為0,則為相對路徑,傳回使用者路徑+此相對路徑。
5. 如果長度為1,則為驅動盤相對路徑,此時嘗試根據使用者路徑擷取驅動盤,存在驅動盤則傳回驅動盤+此路徑,不存在驅動盤則說明使用者路徑是一個 UNC 路徑,傳回使用者路徑+此路徑。
6. 如果頭部長度為2,則為目錄相對路徑。此時先擷取使用者路徑,再根據使用者路徑擷取對應驅動盤,如果路徑以驅動盤開頭,則直接傳回使用者路徑+去掉驅動盤後的路徑。如果繼續往下則通過 getDriveDirectory 擷取指定驅動盤的工作目錄,将驅動盤+<code>:</code>+工作目錄+路徑等拼接起來得到最終的新路徑,然後還要用安全管理器檢查是否有讀的權限。
以下是廣告
========廣告時間========
<a href="http://blog.csdn.net/wangyangzhizhou/article/details/74080321" target="_blank">為什麼寫《Tomcat核心設計剖析》</a>
=========================
歡迎關注:
