我可以相信 Files 中的方法会在预期时抛出 NoSuchFileException 吗?

Can I trust methods in Files will throw NoSuchFileException when expected?

java.nio.file.Files API is a really nice improvement over the old java.io.File class, but one detail strikes me as odd; with the exception of delete() 没有方法记录他们可能抛出 NoSuchFileException,甚至 delete() 说这是可选的。

我希望能够区分由于丢失文件和其他 IO 问题导致的失败,但似乎不能保证这会成为可能。

如果文件是在两个操作之间创建的,那么预先调用 Files.exists() 等的替代方法可能会出现竞争条件。

我可以期望 Files 中的方法会在适当的时候引发 NoSuchFileException 吗?如果是这样,这在哪里记录?如果不是,我怎样才能安全地确定失败是由于文件丢失造成的?


示例: 在 Windows 7 和 Java 7.0.02 上,Files.readAllLines() 方法确实引发了 NoSuchFileException,但是没有明确记录这样做:

Files.readAllLines(Paths.get("foo"), StandardCharsets.UTF_8)
java.nio.file.NoSuchFileException: foo
  at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:79)
  at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
  at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)
  at sun.nio.fs.WindowsFileSystemProvider.newByteChannel(WindowsFileSystemProvider.java:229)
  at java.nio.file.Files.newByteChannel(Files.java:315)
  at java.nio.file.Files.newByteChannel(Files.java:361)
  at java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:380)
  at java.nio.file.Files.newInputStream(Files.java:106)
  at java.nio.file.Files.newBufferedReader(Files.java:2660)
  at java.nio.file.Files.readAllLines(Files.java:2993)

文件class提供两种删除方式

delete(Path)方法删除文件或删除失败抛出异常。例如,如果文件不存在,则抛出 NoSuchFileException。您可以捕获异常以确定删除失败的原因,如下所示:

try {
    Files.delete(path);
} catch (NoSuchFileException x) {
    System.err.format("%s: no such" + " file or directory%n", path);
} catch (DirectoryNotEmptyException x) {
    System.err.format("%s not empty%n", path);
} catch (IOException x) {
    // File permission problems are caught here.
    System.err.println(x);
}

deleteIfExists(Path)方法也会删除文件,但如果文件不存在,则不会抛出异常。当您有多个线程删除文件并且您不想仅仅因为一个线程先执行此操作而抛出异常时,静默失败非常有用。

注意: NoSuchFileExceptionDirectoryNotEmptyException 是 [=31= 中引入的新异常] 7.

一般来说:不,您不能相信 java.nio.file.Files 中的方法会在预期时抛出 NoSuchFileException,但您可以验证。

正如您从堆栈跟踪中看到的那样,Files 使用了 FileSystemProvider to perform the file operations. The FileSystemProvider implementations are restricted (like the WindowsFileSystemProvider) and in turn use a lot of native (C) code. For example, I traced the NoSuchFileException to the WindowsException which relies on the operating system to report a ERROR_FILE_NOT_FOUND or ERROR_PATH_NOT_FOUND. Another example is the newInputStream route which goes from ChannelInputStream to WindowsChannelFactory to WindowsNativeDispatcher.c which finally calls the native Windows function CreateFileW
考虑到 Windows 所涉及的代码量,我不明白如何相信它可以像 Linux 一样工作,例如 Linux 使用代码 here and here

根据我的经验,Linux (Posix) 文件系统行为非常一致,但 Windows (NT) 文件系统行为则不同:我不认为 Windows 7 的行为与 Windows 8.

完全相同

最后,关于竞争条件的评论:我所知道的文件系统都不能保证目录中列出的文件实际存在(某些文件可能已经被删除,尤其是在使用多个线程操作同一目录中的文件时) .根据我的经验,事先使用 Files.exists() 之类的方法不是一个好主意,除非您要分配大量资源(例如,创建连接以上传文件)。处理文件时,最好假设一切都井井有条并捕获异常,然后尝试确定错误所在。例如。读取文件时,打开它而不检查文件是否存在,如果发现错误,请先检查文件是否存在。这可以节省大量 I/O 操作,进而有助于提高性能。