天天看点

JDK不同操作系统的FileSystem(Windows)上篇

我们知道不同的操作系统有各自的文件系统,这些文件系统又存在很多差异,而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>

=========================

欢迎关注:

JDK不同操作系统的FileSystem(Windows)上篇
JDK不同操作系统的FileSystem(Windows)上篇