Java中的getPath()、getAbsolutePath()和getCanonicalPath()有什么区别?

644

在Java中,getPath()getAbsolutePath()getCanonicalPath()有什么区别?

我该何时使用它们?


2
不要忘记使用 Path.toAbsolutePath().normalize(),它是规范化路径和绝对路径之间的一个很好的折中方案。 - eckes
6个回答

673

考虑以下文件名:

C:\temp\file.txt - 这是一个路径,绝对路径和规范路径。

.\file.txt - 这是一个路径。它既不是绝对路径也不是规范路径。

C:\temp\myapp\bin\..\\..\file.txt - 这是一个路径和绝对路径。但它不是规范路径。

规范路径总是绝对路径。

将路径转换为规范路径使其成为绝对路径(通常附加上当前工作目录,因此./file.txt 变为 c:/temp/file.txt)。文件的规范路径只是“净化”路径,删除并解析像 ..\这样的内容并解析符号链接(在Unix上)。

还请注意以下使用nio.Paths的示例:

String canonical_path_string = "C:\\Windows\\System32\\";
String absolute_path_string = "C:\\Windows\\System32\\drivers\\..\\";

System.out.println(Paths.get(canonical_path_string).getParent());
System.out.println(Paths.get(absolute_path_string).getParent());
虽然这两个路径指向同一个位置,但输出结果会有很大的不同:
C:\Windows
C:\Windows\System32\drivers

11
值得一提的是,C:\temp\file.txt 不一定是一个规范路径 —— 临时目录可能是一个文件系统软链接或硬链接(在NTFS中称为联接),而 file.txt 可能是一个软链接。我不知道文件系统是否可以区分对文件的硬链接。 - Lawrence Dol
1
路径并没有真正考虑这些问题或任何组件的存在,只考虑了其语法。 - escape-llc
1
它确实会访问文件系统(与标准化路径不同),以获取规范路径。 - eckes
2
基本上,我看不出为什么应该使用 getAbsolutePath() 而不是 getCanonicalPath()。这甚至看起来更好,因为规范化的路径自动解析那些 ../ 部分。 - Scadge
6
请注意,如果需要考虑此事,请记住 getCanonicalPath 抛出 IOExceptiongetAbsolutePath 不会抛出异常。 - Abandoned Cart

141

我发现最好的方法是尝试一下,这样可以更好地理解:

import java.io.File;
public class PathTesting {
    public static void main(String [] args) {
        File f = new File("test/.././file.txt");
        System.out.println(f.getPath());
        System.out.println(f.getAbsolutePath());
        try {
            System.out.println(f.getCanonicalPath());
        }
        catch(Exception e) {}
    }
}

你的输出结果将类似于:

test\..\.\file.txt
C:\projects\sandbox\trunk\test\..\.\file.txt
C:\projects\sandbox\trunk\file.txt

因此,getPath() 根据文件对象提供路径,该路径可能是相对路径,也可能不是; getAbsolutePath()为您提供了文件的绝对路径;getCanonicalPath()为您提供了文件的唯一绝对路径。请注意,有大量指向同一文件的绝对路径,但只有一条规范路径。

何时使用每个方法?这取决于您要完成的任务,但是如果您想查看两个 Files 是否指向磁盘上的同一文件,可以比较它们的规范路径,这只是其中一个例子。


7
有争议认为Java的"absolute"路径实现有误;它应该在绝对路径中删除任何相对路径元素。规范化形式会删除路径中的任何文件系统链接或联接。 - Lawrence Dol
但如果您想要查看两个文件是否指向磁盘上的同一文件,该怎么做?请举个例子。 - Asif Mushtaq
@UnKnown:你需要使用规范路径。 - Lawrence Dol
问题... 你什么时候会想要使用绝对路径而不是规范路径?检查规范路径在更多情况下是否“更好用”? - Collin

87
简而言之:
  • getPath() 获取 File 对象构造时的路径字符串,该路径可能是相对于当前目录的。
  • getAbsolutePath() 获取在解析相对路径后与当前目录合并的完全限定路径字符串。
  • getCanonicalPath() 获取解析任何相对路径并删除任何相对路径 (...) 以及任何文件系统链接后的路径字符串,返回文件系统认为引用其指向的文件系统对象的规范方式。

此外,每个方法都有一个返回相应 File 对象的等效方法。

请注意,我个人认为 Java 实现的“绝对”路径有误; 它实际上应该删除绝对路径中的任何相对路径元素,而规范形式应删除路径中的任何文件系统链接或连接点。


1
我发现这个解释非常清晰易懂。谢谢! - Dude156

41

getPath() 返回用于创建File对象的路径。该返回值不会因其运行的位置而更改(下面的结果适用于Windows,分隔符在其他地方显然是不同的)。

File f1 = new File("/some/path");
String path = f1.getPath(); // will return "\some\path"

File dir = new File("/basedir");
File f2 = new File(dir, "/some/path");
path = f2.getPath(); // will return "\basedir\some\path"

File f3 = new File("./some/path");
path = f3.getPath(); // will return ".\some\path"

getAbsolutePath() 方法将根据执行位置或驱动器解析路径。因此,如果从 c:\test 运行:

path = f1.getAbsolutePath(); // will return "c:\some\path"
path = f2.getAbsolutePath(); // will return "c:\basedir\some\path"
path = f3.getAbsolutePath(); // will return "c:\test\.\basedir\some\path"

getCanonicalPath() 是与系统相关的。它将解析路径所表示的唯一位置。因此,如果路径中有任何“.”,它们通常会被移除。

至于何时使用它们,则取决于您要实现什么目标。 getPath() 对于可移植性非常有用。 getAbsolutePath() 用于查找文件系统位置,而getCanonicalPath() 则特别适用于检查两个文件是否相同。


你能给我一个例子吗?getCanonicalPath()特别有用,可以检查两个文件是否相同。 - Asif Mushtaq
所以基本上,如果你希望你的代码可以在多个操作系统上运行,那么你应该使用getCanonicalPath()吗? - Collin

23

理解的重点是,File类试图表示Sun所谓的“分层路径名”的视图(基本上像c:/foo.txt/usr/muggins这样的路径)。这就是为什么你以路径的形式创建文件。你所描述的操作都是在这个“路径名”上进行的。

  • getPath()获取创建文件时使用的路径(../foo.txt
  • getAbsolutePath()获取创建文件时使用的路径,但如果路径是相对路径,则包括有关当前目录的信息(/usr/bobstuff/../foo.txt
  • getCanonicalPath() 试图获取文件绝对路径的唯一表示。这消除了“..”和“.”引用的间接性(/usr/foo.txt)。

请注意我说的是尝试 - 在形成规范路径时,虚拟机可能会抛出IOException。这通常是因为它执行一些文件系统操作,其中任何一个可能失败。


3

我发现我很少需要使用getCanonicalPath()方法,但是如果在Windows上给定一个文件名为DOS 8.3格式的文件,例如java.io.tmpdir系统属性返回的文件,则此方法将返回“完整”文件名。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接