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 ? false
行sub_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");