Java识别出一些文件和目录但仍然认为它们不存在

Gar*_*son 19 java file

我在采用 NTFS 的 Windows 10 上使用 Java 17。有一个文件A:\\foo.bar显示在 JavaFiles.list()操作中,但是当我尝试使用 Java 读取它时,它会抛出一个java.nio.file.FileSystemException:

\n
\n

A:\\foo.bar: 该进程无法访问该文件,因为该文件正在被另一个进程使用

\n
\n

没关系。还有另一个低级进程已锁定该文件。当我关闭其他进程时,Java 可以正常访问该文件。事实上,即使 Robocopy 在尝试访问该文件时也会跳过该文件(实际上 Robocopy 会显示已复制该文件,但实际上并未复制)。所以到目前为止,这并不神秘\xe2\x80\x94另一个进程正在锁定该文件以进行独占访问。

\n

但这是奇怪的部分。大多数情况下,该文件对于 Java 来说是正常的:

\n
    \n
  • 正如我提到的,该文件A:\\foo.bar显示Files.list()在A:\\.
  • \n
  • 如果我调用Files.isRegularFile(fooBarFile),它会true按预期返回。
  • \n
  • 如果我调用Files.isReadable(fooBarFile),它会false按照我的预期进行转换(这在本例中很有用)。
  • \n
  • 如果我打电话,Files.readAttributes(fooBarFile, "*")我会看到属性(时间戳等)。
  • \n
  • 如果我调用Files.readAttributes(fooBarFile, DosFileAttributes.class)它会返回 DOS 属性。
  • \n
\n

但如果我调用Files.exists(fooBarFile)它就会返回false!因此,被另一个进程锁定为独占访问的文件将返回falsefor exists(),在我看来,这似乎不遵循exists()其 API 中解释的方法的语义。

\n

事实上,通过检查是否exists()返回false来查看文件是否不可isRegularFile()访问似乎确实有用true;然而,这是出乎意料的,而且似乎没有记录。这是预期的行为吗?有记录吗?它在其他平台(例如Linux)上的工作方式是否相同?

\n

最后我注意到它也Files.notExists(fooBarFile)返回false,所以Java并不是说该文件不存在,只是说它不存在。嗯 \xe2\x80\xa6 我能理解的唯一方法是 ifexists()意味着“可访问”,但 API 契约exists()没有谈论可访问性。该notExists()文档添加:

\n
\n

请注意,此方法不是exists 方法的补充。如果无法确定文件是否存在,则两种方法都返回 false。

\n
\n

这似乎也不适用于这种情况。因此,尽管这种行为很有用,但它是出乎意料的,似乎没有记录在案,因此我对是否过度依赖它感到犹豫。谁能提供更多信息或更好且权威的文档?

\n

更新:类似的事情似乎发生在不可读的目录中。例如,该A:\\System Volume Information目录在 Windows 上被标记为“隐藏”和“只读”。java.nio.file.FileSystemException如果您尝试访问它,它也会类似地引发异常。但Files.exists()归来false,纵然Files.isDirectory()归来true!事实上,它的行为与上面的要点完全相同,除了isDirectory()returntrue而不是isRegularFile()。

\n

因此,它看起来像是Files.exists()重复的Files.isReadable()(至少在 Windows 上的 OpenJDK 17 上),即使这种行为似乎不遵循Files.exists()API 契约。这是 JDK 的错误吗?

\n

Pan*_*kos 11

因此,看起来 Files.exists() 正在复制 Files.isReadable() (至少在 Windows 上的 OpenJDK 17 上),即使该行为似乎不遵循 Files.exists() API 契约。这是 JDK 的错误吗?

长话短说

这就是它的设计行为方式,并且遵循 API 契约。我们有两种方法的一个主要原因是,如果两种方法都true确定问题,那么它们都会返回。

  • Files.exist()true 表示文件确实存在。
  • Files.notExists()true 表示文件绝对不存在。

但虚假并不意味着相反。这也可能意味着系统无法确定结果,如果它不能抛出这样的检查异常,它会返回 false 作为最安全的返回。

  • 如果你想做一些基于系统中不存在该文件的条件的操作,你不应该使用 If (!Files.exists(file)){...}。在这种情况下,假设您想要创建一个新文件,并且想要确保另一个文件尚不存在,您应该始终检查 If(Files.notExists(file)){...}

  • 另一方面,如果您想根据系统中存在文件的条件执行某些操作,则永远不应该使用 If (!Files.notExists(file)){...}。在这种情况下,假设您想要读取/修改一个文件,并且想要确保该文件已经存在,您应该始终检查 If(Files.exists(file)){...}

分析说明

我相信 java API文档可能Files.exists(fooBarFile)会更好一点,尽管 java API文档在以下方面对此notExists更加清晰:

请注意,此方法不是exists 方法的补充。如果无法确定文件是否存在,则 两种方法都会返回 false。

这就是我怀疑正在发生的事情。

根据方法说明

//  @return  
// {@code true} if the file exists; 
// {@code false} if the file does not exist or its existence cannot be determined.
public static boolean exists(Path path, LinkOption... options) {
        if (options.length == 0) {
            FileSystemProvider provider = provider(path);
            if (provider instanceof AbstractFileSystemProvider)
                return ((AbstractFileSystemProvider)provider).exists(path);
        }

        try {
            if (followLinks(options)) {
                provider(path).checkAccess(path); <-------------- if no options provided this is executed
            } else {
                // attempt to read attributes without following links
                readAttributes(path, BasicFileAttributes.class,
                               LinkOption.NOFOLLOW_LINKS);
            }
            // file exists
            return true;
        } catch (IOException x) {
            // does not exist or unable to determine if file exists
            return false;  <----- AccessDeniedException is causing return false
        }

    }
Run Code Online (Sandbox Code Playgroud)

如果有人检查代码,他会看到的含义@return {@code false}... or its existence cannot be determined.

可以从.checkAccess(path)API 文档中提到的行进一步解释:

 * @throws  AccessDeniedException
 *          the requested access would be denied or the access cannot be
 *          determined because the Java virtual machine has insufficient
 *          privileges or other reasons. <i>(optional specific exception)</i>
Run Code Online (Sandbox Code Playgroud)

因此,如果AccessDeniedException抛出 a ,它也是 a 的子类FileSystemException(也是提问者在文件读取期间从另一个命令获取的),那么Files.exists(fooBarFile)应该返回false,因为该异常不允许 JVM 确定文件是否确实存在。

问题还提到:

文件。notExists (fooBarFile) 也返回 false

我认为这也源于同一点。

public static boolean notExists(Path path, LinkOption... options) {
        try {
            if (followLinks(options)) {
                provider(path).checkAccess(path);  <---------- If no options provided this line executes
            } else {
                // attempt to read attributes without following links
                readAttributes(path, BasicFileAttributes.class,
                               LinkOption.NOFOLLOW_LINKS);
            }
            // file exists
            return false;
        } catch (NoSuchFileException x) {
            // file confirmed not to exist
            return true;
        } catch (IOException x) { 
            return false;   <------ In case of AccessDeniedException or some other IOException the return is false.
        }
    }
Run Code Online (Sandbox Code Playgroud)

这验证了这两个方法exists和 都notExists被设计为false在抛出异常AccessDeniedException或其他异常时返回IOException,因为这将被转换为 , JVM无法确定文件是否存在。

因此,考虑到它不能抛出这样的异常,每个方法都会在这种不确定的情况下给出最安全的返回。所以万一

  • 如果无法确定则exists()返回false,这样开发人员就无法确定该文件是否存在并继续执行读取文件等操作。
  • 如果无法确定,它就会notExists()返回false,这样开发人员就无法确定该文件不存在并继续执行创建新文件等操作。

因此,这两种方法都应适当使用,并且只有在它们返回时true才能得到明确的答案。false并不意味着对exists或 的否定回答notExists。并且某人永远不应该基于!Files.exists(file)和的结果!Files.notExists(file),至少作为该方法答案的逻辑反转。