尝试制作一个简单的'cut'程序来跨文件夹移动文件。在它复制后,它应该删除源文件,但它忽略fileLocation.delete();方法在try块中。如果我把它放在'finally'块中,它可以工作,并且在程序中的其他任何地方,在它复制文件之后,它也可以工作,但这对它以这种方式工作没有意义,即使出了问题,源也会被删除。我的问题是为什么它忽略它,我无法在网上找到答案。谢谢你。
File fileLocation = new File("C:\fileLocation\picture.png");
File fileDestination = new File("C:\fileDestination\picture.png");
try(FileInputStream input = new FileInputStream(fileLocation);
FileOutputStream output = new FileOutputStream(fileDestination)) {
byte[] buffer = new byte[1024];
int length;
while((length = input.read(buffer)) > 0) {
output.write(buffer,0, length);
}
fileLocation.delete();
} catch(IOException exc) {
System.out.println(exc.getMessage());
}
try(FileInputStream input = new FileInputStream(fileLocation);
... ) {
// ..
fileLocation.delete();
}
此时,input
仍然是打开的,所以您不能删除它所引用的文件。
根据语言规范中try-with-resources的定义,try-with-resources语句上的finally
块将在资源关闭后执行。因此,将删除放在finally
块中意味着它可以成功。
与其将其放在finally
中(无论是否抛出异常都会发生),不如将资源拆分为两个尝试使用资源块,并在完成input
后删除:
try (FileOutputStream output = ...) {
try (FileInputStream input = new FileInputStream(fileLocation)) {
// ..
}
// input is now closed.
fileLocation.delete();
} catch(IOException exc) {
System.out.println(exc.getMessage());
}
现在,只有在output
的try-with-resources块(包括input
的try-with-resources块)中没有抛出IOException
时,fileLocation
才会被删除。
或者,如果您想在output
关闭之前不删除它:将IOException
捕获移动到周围的try/catch块中(而不是try-with-resources):
try {
try (FileOutputStream output = ...;
FileInputStream input = ...) {
// ..
}
// input and output are now both closed.
fileLocation.delete();
} catch(IOException exc) {
System.out.println(exc.getMessage());
}
当然,移动文件更好的方法是使用实用程序方法来移动文件,例如
Files.move(fileLocation.toPath(), fileDestination.toPath(), CopyOption.REPLACE_EXISTING);
您使用了错误的API。File.delete()
是众所周知的坏API设计。
这就是它不好的地方,也是为什么它解释了你的困惑:不像任何其他API,如果delete()
删除失败,它不会抛出任何异常。相反,它返回false
。这在三个重要方面是不好的:
- un-java-like。很少有api能做到这一点;他们中的绝大多数人会扔东西。
- 很容易"忘记"。只是自己编写
x.foo();
,其中foo()
是返回某些东西的任何方法(即具有非void
返回类型),这是完美的java。这是java的for:运行这个方法,然后获取结果并将其扔进垃圾桶。您在这里已经这样做了:调用delete()
并忽略结果。对于delete(),这实际上是不行的,除非您打算编写的代码有效地表示:">try删除该路径。无论成功与否,都要继续执行代码。这通常不是你想要的。 - 如果确实出错,则不可能让
delete()
方法告诉你除了"我无法完成它"之外的任何细节。没有办法使用消息或某种异常类型来为您清除问题。
解决方案很简单。停止使用这种方法。把它放在列表中:这个方法不应该再在java代码中调用。如果你在维护一些有15年历史的东西,我想这很好,但快速重构来摆脱它也不会错。
很棒!那么我应该用哪个新符号呢?
java.nio.file
包中的path/files API。
替换:
File f = new File("a/b/c.txt");
f.delete();
:
Path p = Paths.get("a/b/c.txt");
Files.delete(p);
不像file.delete()
,Files.delete(path)
将抛出一个异常,如果不能执行删除。然后,该异常包含有关原因的适当信息。例如,因为文件不存在,或者因为您没有对底层目录的写访问权限,或者因为文件系统是以只读方式挂载的,等等。
新的File API也更加强大。例如,它可以正确地处理链接或备用文件系统。它也有更多的方法。例如,它有Files.move
方法,在这里可能特别有用。
仅供参考,为什么我的删除操作失败?
可能是因为您自己的进程仍然打开着文件。在某些操作系统/文件系统组合(特别是在windows和NTFS上),您不能删除打开的文件。即使你自己的进程仍然打开着文件
如果你使用Files.delete(),你会得到一个异常,消息会让你更接近那个结论,而不是'delete()
调用返回false
',幸运的是。