Java在Windows下的"绝对路径", "完全相对路径" 和 "目录相对路径"等路径表示法

时间:2022-11-27 15:35:56

额, 不得不说, 论坛是个好地方, 每隔几天总有很好的问题出现. 这篇博客还是因为一个有意思的帖子. 这次的问题和路径的表示方法有关. 帖子在这里: 《求java大神,关于se中的文件操作

注: “完全相对路径”和”目录相对路径” 的英文分别为 “Completely relative”, “Directory-relative”, 限于本人英文水平, 翻译的可能有些问题, 还请大家指导

原问题

帖子地址: http://bbs.csdn.net/topics/391025871.
问题的大致意思是说: 当使用File(String pathname)去构造一个File对象是, 参数"e:""e:\\"有什么区别?
估计大多数人和我最开始的想法一样: 没区别. 然后下面就有位仁兄截了个图, 证明这两个是有区别的. 本着实事求是的精神, 我也测了测, 发现当java程序的当前路径在e盘时, "e:""e:\\"还真不一样…

源程序

为了方便说明问题, 这里的源程序跟帖子上稍微有点区别.

import java.io.*;

class Test
{
public static void main(String[] args)
{
digui(new File("e:"));
digui(new File("e:\\"));
}

public static void digui(File f)
{
System.out.println(f.getPath());
if( !f.exists() ){
System.out.println("not exist");
return;
}
File [] name = f.listFiles();
System.out.println(name.length);
for(int i = 0 ;i < name.length; i++)
{
System.out.println(name[i]);
}
}
}

将这个程序中的 "e:""e:\\"改成自己的工作盘符, 运行程序, 就会发现这两个路径的区别…

API文档上有关的说明

为了解释这个现象, 我首先查看了File类的文档, 看看有没有什么特殊说明. 只是发现了这样一句话

对于 Microsoft Windows 平台,包含盘符的路径名前缀由驱动器号和一个 “:” 组成。如果路径名是绝对路径名,还可能后跟 “\\”。

注意, 上面说如果是绝对路径名, 还可能"\\". 也就是说, 即使是以 驱动器号+”:” 开始的路径, 也不一定是绝对路径!
同时,经过测试, getPath方法得到的并不是完整的路径!仅仅是把我们传进去的参数标准化了而已.
我们在API中发现两个可以替代的方法: getAbsolutePath方法, 以及getCanonicalPath方法. 这里我们只使用getAbsolutePath方法

public String getAbsolutePath()
返回此抽象路径名的绝对路径名字符串。
(以下省略n行)…

更详细的信息, 请查看JDK API文档.
然后我们修改一下我们的程序.将

System.out.println(f.getPath());

修改为

System.out.println("path:[" + f.getPath() + "] absolutePath:[" + f.getAbsolutePath() + "]");

从API文档只能得到这么多信息了, 为了的到更加详细的信息, 接下来, 我们看看Java类库的源码.(我的是jdk 1.8.0_45附带的源码)

从Java类库源码得到的信息

从输出来看, getAbsolutePath方法输出的就是File对象所表示的路径, 那么就让我们先看看File类的构造方法, 然后再看看getAbsolutePath方法. 可能就会得到我们想要的信息.

File类的构造方法

首先, 我们看看File(String pathname)方法的源码:

public File(String pathname) {
if (pathname == null) {
throw new NullPointerException();
}
this.path = fs.normalize(pathname);
this.prefixLength = fs.prefixLength(this.path);
}

这里的normalize(pathname)是标准化路径. 我们就不继续向下查看的. 对于我们讨论的这两种路径表示方法, 返回值都是原本的值.

getAbsolutePath方法

由于getAbsolutePath里需要使用到prefixLength属性. 同时在Windows下, 实现FileSystem接口的类是:WinNTFileSystem类. 所以, 我们看看WinNTFileSystem.prefixLength(path)方法:
java.io.WinNTFileSystem.java

public int prefixLength(String path) {
char slash = this.slash;
int n = path.length();
if (n == 0) return 0;
char c0 = path.charAt(0);
char c1 = (n > 1) ? path.charAt(1) : 0;
if (c0 == slash) {
if (c1 == slash) return 2; /* Absolute UNC pathname "\\\\foo" */
return 1; /* Drive-relative "\\foo" */
}
if (isLetter(c0) && (c1 == ':')) {
if ((n > 2) && (path.charAt(2) == slash))
return 3; /* Absolute local pathname "z:\\foo" */
return 2; /* Directory-relative "z:foo" */
}
return 0; /* Completely relative */
}

从上面的代码来看

  • "e:\\"对应的File对象的prefixLength属性为3
  • "e:"对应的File对象的prefixLength属性为2

FileSystem.resolve方法

由于File.getAbsolutePath方法直接调用了FileSystem.resolve. 所以,我们查看一下WinNTFileSystem.resolve方法:

public String resolve(File f) {
String path = f.getPath();
int pl = f.getPrefixLength();
if ((pl == 2) && (path.charAt(0) == slash))
return path; /* UNC */
if (pl == 3)
return path; /* Absolute local */
if (pl == 0)
return getUserPath() + slashify(path); /* Completely relative */
if (pl == 1) { /* Drive-relative */
String up = getUserPath();
String ud = getDrive(up);
if (ud != null) return ud + path;
return up + path; /* User dir is a UNC path */
}
if (pl == 2) { /* Directory-relative */
String up = getUserPath();
String ud = getDrive(up);
if ((ud != null) && path.startsWith(ud))
return up + slashify(path.substring(2));
char drive = path.charAt(0);
String dir = getDriveDirectory(drive);
...
return drive + ":" + slashify(path.substring(2)); /* fake it */
}
throw new InternalError("Unresolvable path: " + path);
}

private String slashify(String p) {
if ((p.length() > 0) && (p.charAt(0) != slash)) return slash + p;
else return p;
}
  • getUserPath方法返回System.getProperty("user.dir"), 也就是当前的工作目录.
  • getDrive返回路径的盘符部分

那么

  • "e:\\"构造的File对象的getAbsolutePath方法返回"e:\\".
  • "e:"构造的File对象的getAbsolutePath方法返回该应用程序在e盘下的工作目录

那么, e盘下的工作目录是什么? 相信大家都在cmd里执行过这几条命令:

C:\Windows\System32>cd e:\win8
C:\Windows\System32>e:
E:\win8>c:
C:\Windows\System32>

这里, E:\\win8就是E盘的工作路径. C:\\Windows\\System32就是C盘的工作路径. 而目录相对路径就是相对于这些目录.
比如:

C:drivers 就是 C:\\Windows\\System32\\drivers
E:eclipse 就是 E:\\win8\\eclipse

但是由于我们没有切换到F盘过, 工作路径也不是F盘, 所以

F:就是F:\\
F:test 就是 F:\\test

所以, File("E:")表示的到底是哪个路径, 跟工作环境有关:

  • 如果当前工作路径不在e盘, 也从来没有移到e盘过, 那么File("E:")File("E:\\")表示同一个目录
  • 如果当前工作路径在e盘, 那么File("E:")表示当前路径
  • 如果曾经切换到e盘过, 那么File("E:")表示最后一次在e盘时的路径.
    F:\test>java Test
    path:[e:\] absolutePath:[e:\]
    8
    输出e:\中的文件(夹)
    path:[e:\] absolutePath:[e:\]
    8
    输出e:\中的文件(夹)

    F:\test>cd e:\win8
    F:\test>e:
    E:\win8>f:
    F:\test>java Test
    path:[e:] absolutePath:[e:\win8]
    20
    输出e:\win8中的文件(夹)
    path:[e:\] absolutePath:[e:\]
    8
    输出e:\中的文件(夹)

Java在Windows下的几种路径表示法

从`WinNTFileSystem.prefixLength`方法中的注释中我们可以看到Windows中的五种路径表示方法 :
  • “Absolute local pathname”(绝对路径)
    • e:\\win8
    • C:\\Windows\\System32
  • “Completely relative”(完全相对路径)
    • win8    当前工作目录下的win8文件(夹)
    • System32  当前工作目录下的System32文件(夹)
    • Windows\\System32 当前工作目录下的Windows\\System32文件(夹)
  • “Directory-relative”(目录相对路径)
    • 就是 盘符 + “:” + 相对路径.
    • e:     e盘的工作目录
    • c:drivers c盘工作目录下的drivers文件(夹)
    • 注意, 在java中使用Properties类的setProperty("user.dir", dir)方法切换当前工作目录的影响有点特殊. 当然如果我们把上面看过的方法及其调用的方法都完整的看完, 那么是可以解释下面的现象的:
System.setProperty("user.dir", "c:\\Windows");
System.out.println(System.getProperty("user.dir"));
digui(new File("c:")); // 输出为c:\\Windows, 也就是当前工作目录
String path = System.getProperty("user.dir"); // 保存当前工作路径
System.setProperty("user.dir", "c:\\Windows");
System.out.println(System.getProperty("user.dir"));
System.setProperty("user.dir", path); // 回复工作路径
System.out.println(System.getProperty("user.dir"));
digui(new File("c:")); // 输出路径为c:\\Windows\\System32, 也就是运行java程序之前的c盘符的工作目录
  • “Drive-relative”(盘符相对路径 或者 驱动器相对路径)
    • \\win8   当前工作盘符下的win8文件(夹)
  • “Absolute UNC pathname”(绝对UNC路径)
    • \\\\c:\\Windows  c:\\Windows
    • 详细信息可以自己搜索UNC

注: linux下只有两种路径表示方法: 相对路径和绝对路径.

写于 2015/04/29