** java.io.File**
new File(path)创建一个文件类对象,path可以是绝对路径,也可以是相对路径。
默认情况下,java.io 包中的类总是根据当前用户目录来解析相对路径名。此目录由系统属性 user.dir 指定,通常是 Java 虚拟机的调用目录。
如果修改了user.dir呢?
@Test
/**
* user.dir 属性? ×
* 结合 test03 可知:
* user.dir 属性:
* 影响 File::getAbsoluteFile() 与 File::getAbsolutePath()
* 但是与File::exists() 无关
*/
public void test02() throws IOException {
String relativePath = "target/test-classes/data/file01.txt";
// debug maven test 时:
// user.dir 为 E:\Documents\IdeaProjects\MyWorkSpace\prj01\modelA
// file.exists() 为 false
// file.getAbsoluteFile.exists() 为 true
System.out.println("original user.dir is: " + System.getProperty("user.dir"));
File file = new File(relativePath);
System.out.println("\tfile exist: " + file.exists() +
"\n\t\tpath=" + file.getPath() +
"\n\t\tabsolutePath=" + file.getAbsolutePath() +
"\n\t\tabsolutrFile exist:" + file.getAbsoluteFile().exists());
// 修改 user.dir 为 e:/ 后:
// file.exists() 为 false
// file.getAbsoluteFile.exists() 为 false
System.setProperty("user.dir", "e:/");
System.out.println("after change user.dir is: " + System.getProperty("user.dir"));
File file1 = new File(relativePath);
System.out.println("\tfile exist: " + file1.exists() +
"\n\t\tpath=" + file1.getPath() +
"\n\t\tabsolutePath=" + file1.getAbsolutePath() +
"\n\t\tabsolutrFile exist:" + file.getAbsoluteFile().exists());
}
测试可知,修改了 user.dir 后,file.exists()还是和之前一样,为true;发生变化的只有 file.getAbsoluteFile()。
解释:
- 如果System Property user.dir被修改,看起来位置变了,但是真正创建(java的io相当于在app和os之前加了一个中间层)文件的时候,os仍然使用了java应用真正启动的当前目录。这个问题在java的bug库中找到了说明,原来system properties中的 user.dir 或者说启动时传的 -Duser.dir 不应该被修改,他们应该是readonly属性(虽然jdk没有做强制的约束,只是口头上的规约)。这是因为jvm启动时候,对于os来说已经记录了一个 CWD(工作路径) ,后面简单地修改它,是不会影响os kernel的,并且也不应该影响(JNI可以,但是jdk官方也不建议这么做)。
- 每次调用 getAbsolutePath() 的时候,如果File是根据相对路径构造的,jvm会根据 user.dir 变量的值加上相对路径做为绝对路径。
进一步验证:
/**
* user.dir 变量 和 File 和 File::getAbsoluteFile 之间有什么关系?
*/
@Ignore
@Test
public void test03() throws IOException {
File f1 = new File("target/test-classes/data/file01.txt");
// 为什么修改了 user.dir ,绝对路径变为 e:\\target\test-classes\data\file01.txt target\test-classes\data\file01.txt 后,
// f1.exists() 还是 true;而 f1.getAbsoluteFile().exists() 为 false?
// 相对路径 相对于 jvm 运行项目时的根目录? 与 user.dir 的关系是什么?
System.out.println(f1.getAbsolutePath() + " " + f1.getPath() + " " + (f1.exists() ? "exists" : "does not exist"));
System.out.println(f1.getAbsolutePath() + " " + (f1.getAbsoluteFile().exists() ? "exists" : "does not exist"));
System.out.println(f1.getAbsolutePath());
// 写入值成功
out(f1, "before\n".getBytes());
out(f1.getAbsoluteFile(), "absolute before\n".getBytes());
System.setProperty("user.dir", "d:/");
System.out.println(f1.getAbsolutePath());
// 写入值成功
out(f1, "after\n".getBytes());
// out(f1.getAbsoluteFile(), "absolute after\n".getBytes()); // java.io.FileNotFoundException: d:\\target\test-classes\data\file01.txt (系统找不到指定的路径。)
}
private void out(File f, byte[] b) throws IOException {
// new FileOutputStream() 允许文件名不存在,但是路径必须存在
FileOutputStream fos = new FileOutputStream(f, true);
fos.write(b);
fos.close();
}
修改了 user.dir 变量后,仍然可以写入值到 file。而写入到 file.getAbsoluteFile()失败。
PS: 最后还有一个问题,jvm可以主动触发产生另一个jvm进程,即 Runtime.getRuntime().exec(String[] cmdarray, String[] envp, File dir) 这里的dir就是子jvm所使用的CWD,如果null的话,他会尝试用当前jvm的CWD来代替。