Java:设置“user.dir”时File.exists()不一致

JRE 6,在Windows XP上。

使用不同构造函数实例化两个File对象会导致File.exists()方法中的结果不一致。

免责声明: 以下代码是摘要,而不是实际代码。 我根本不相信这是一个File.separator问题。 我首先要求得到早期反应,以防我错过了一个很好理解的问题。 现在看来,重置user.dir系统属性是导致此问题的原因之一。 下面的代码现在可以重现并且可以按原样使用。 您可以复制/粘贴Java类并尝试它,它应该与我列出的结果一致。

建立:

创建文件夹架构C:\toto\tmp\sub

从任何不包含tmp/sub子文件夹体系结构的文件夹中启动以下类。

码:

 public class TestFileExists { public static void main(String[] args) { System.setProperty("user.dir", "C:\\toto\\"); File root = new File("tmp"); File sub_a = new File(root, "sub"); File sub_b = new File(root.getAbsolutePath()+"/sub"); System.out.println("sub_a path ? "+sub_a.getAbsolutePath()); System.out.println("sub_a exists ? "+sub_a.exists()); System.out.println("sub_b path ? "+sub_b.getAbsolutePath()); System.out.println("sub_b exists ? "+sub_b.exists()); System.out.println("Path equals ? "+ (sub_a.getAbsolutePath().equals(sub_b.getAbsolutePath()))); System.out.println("Obj equals ? "+ (sub_a.equals(sub_b))); } } 

结果:

 sub_a path ? C:\toto\tmp\sub sub_a exists ? false sub_b path ? C:\toto\tmp\sub sub_b exists ? true Path equals ? true Obj equals ? false 

我不明白sub_a exists ? falsesub_a exists ? false sub_a exists ? false并且结果在机器之间不一致,也不与根初始路径一致,结果现在与机器一致。

现在,如果通过从命令行调用java来重新执行该类,则从包含tmp/sub子文件夹体系结构的文件夹(如果从D:\调用它,具有D:\tmp\sub ),您将得到预期的:

 sub_a path ? C:\toto\tmp\sub sub_a exists ? true sub_b path ? C:\toto\tmp\sub sub_b exists ? true Path equals ? true Obj equals ? false 

但是sub_a的存在显然是误报,因为它检查了另一个文件夹的存在,而不是getAbsolutePath()描述的文件夹。

所以我强烈怀疑File.exists()依赖于实际的Java执行路径,并且该文件存在与绝对路径不一致,而exists()使用另一个路径而不是“user.dir”系统属性来检查文件系统。

知道这个问题可能来自哪里?

不支持设置user.dir 。 它应该被视为只读属性。

例如,Sun Bug Parade中对Bug 4117557的评估包含以下文本:

在jvm启动期间初始化的“user.dir”应该用作信息性/只读系统属性,尝试通过命令行自定义它-Duser.dir = xyz将最终执行dependend / unspecified行为。

虽然本文是关于在命令行上设置它,但通过setProperty()设置它很可能同样未定义。

如果您可以在手动设置user.dir 情况下重现问题,那么您就发现了一个真正的问题。

在测试中添加以下行:

 System.out.println("sub_a = " + sub_a); System.out.println("sub_b = " + sub_b); 

结论:

1)sub_a是相对路径,sub_b是绝对路径。

2) exists()不使用绝对路径

3)设置环境变量user.dir不会更改当前用户目录,由exists()

4)getAbsolutePath使用user.dir变量而不是真实的当前用户目录! 至少对于Windows(请参阅Win32FileSystem.getuserPath)。 那就是问题所在! (错误?)

File可以表示抽象路径。 为tmp创建一个File ,从tmp的绝对路径创建一个File将不等于File对象,尽管它们的绝对路径是相等的。

我不完全确定sub_a不存在的情况但sub_b会但我怀疑它是一个分隔符问题。 我怀疑它与来自文件(文件,字符串)的javadoc的这个语句有关:

每个路径名字符串都转换为抽象路径名,子抽象路径名将根据父路径进行解析。

我不知道基于Unix的文件系统的情况,从/full/path/to ,。 /full/path/to/tmp ./tmp/sub将存在但/full/path/to/tmp不存在。

如果系统之间的问题是一致的,可以通过转储每个File对象的状态而不仅仅是打印比较来更清楚地理解它。

在这一行:

 File sub_b = new File(root.getAbsolutePath()+"/sub"); 

您应该使用常量File.separator (取决于底层系统)而不是硬编码的正斜杠。

我认为它是目录分隔符,因为Windows通常使用反斜杠“\”而Linux使用普通斜杠“/”。

尝试将线路更改为:

 File sub_b = new File(root.getAbsolutePath() + System.getProperty("file.separator") + "sub"); 

这可能是文件分隔符问题。 在Windows上,标准文件分隔符是反斜杠( \ )。 创建sub_b您创建一个包含斜杠和反斜杠的路径名(作为String )。 系统可能会对此感到困惑。

这些结果应该是跨机器的确定性的。 让我逐行打破这个:

 System.out.println("sub_a exists ? "+sub_a.exists()); 

在这里,您要问的是文件系统中是否存在此文件。 假设文件存在,这应始终返回相同的值。

 System.out.println("sub_b exists ? "+sub_b.exists()); 

一样。 您正在检查此文件是否确实存在。

 System.out.println("Path equals ? "+ (sub_a.getAbsolutePath().equals(sub_b.getAbsolutePath()))); 

在这里,您将看到AbsolutePath是否相同。

 System.out.println("Obj equals ? "+ (sub_a.equals(sub_b))); 

在这里,你正在与.equals()进行对象比较,它们会调用FileSystem类来执行两个对象的路径对象的compare(),而不是它们的AbsolutePath

可能是文件分隔符错误。 尝试在构造sub_b时替换File.separator,如下所示:

 File sub_b = new File(root.getAbsolutePath()+File.separator+"sub");