java.io.File.listFiles 与 java.nio.Files.list 及其抛出的 IOException
java.io.File.listFiles vs java.nio.Files.list and its thrown IOException
为什么 Files.list
会抛出 IOException
而 File.listFiles
不会?
查看Files.list
(Java 8) 的源代码,我更好奇为什么没有抛出一个UncheckedIOException
因为它也在迭代器中抛出。
如果我用 Files.list
替换 File.listFiles
-code,我现在需要处理一个异常,而我以前并没有真正处理过。
不用说,大多数开发人员甚至都不知道他们当时必须处理什么 ;-) 或者只是放一个 // TODO
and/or e.printStackTrace()
在那里。
这使得在此处使用 Stream
相当麻烦,因为您需要用 try/catch
包围它或重新抛出异常,这在遗留代码中甚至可能是不可能的。
那么为什么做出这个决定?
首先,开发者总是必须处理可能发生的错误情况。
-
Returns null
if this abstract pathname does not denote a directory, or if an I/O error occurs.
-
Throws:
NotDirectoryException
- if the file could not otherwise be opened because it is not a directory (optional specific exception)
IOException
- if an I/O error occurs when opening the directory
所以不同之处在于,开发人员可能很容易忘记对 null
的检查,只要没有错误条件,就永远不会被注意到。当问题发生在客户身上并且您的应用程序抛出一个 NullPointerException
而不是处理一个可能微不足道的问题时,您将通过艰难的方式学习它。
您的陈述“不用说,大多数开发人员甚至不知道他们当时必须处理什么”,这很好地说明了这一点。确实如此,但编译器会在编译时告诉您,您必须处理 IOException
。与 File.list()
不同,检查 null
的失败可能会被忽略。在这两种情况中,你仍然可能处理得不好,但没有办法避免这种情况。
当然,一旦你明白你必须处理这个问题,你可能会问你想如何处理它,这取决于问题的种类。 return 值为 null
并不能说明问题所在。您可以通过 File.isDirectory()
检查“不是目录”条件并希望它在这期间没有改变,但是如果文件 是 一个目录,您不需要没有任何关于从哪里开始的提示。
相比之下,抛出的IOException
不仅可以让你区分NotDirectoryException
和其他错误情况,IOException
是一个森林的基础class可以准确描述问题的特定异常,例如AccessDeniedException
。即使异常有一个不明确的类型,它也可能有一个有意义的消息,你可以呈现给用户。
请注意,这是具有以下两个 API 的一般模式:
-
Returns:
true
if and only if the renaming succeeded; false
otherwise
-
Returns:
true
if and only if the file or directory is successfully deleted; false
otherwise
那么当这些方法中的任何一个 returns false
时,你会怎么做?
Files.move(Path,Path,CopyOption...)
Throws:
…
FileAlreadyExistsException
- if the target file exists but cannot be replaced because the REPLACE_EXISTING
option is not specified (optional specific exception)
DirectoryNotEmptyException
- the REPLACE_EXISTING
option is specified but the file cannot be replaced because it is a non-empty directory (optional specific exception)
AtomicMoveNotSupportedException
- if the options array contains the ATOMIC_MOVE
option but the file cannot be moved as an atomic file system operation.
IOException
- if an I/O error occurs
这就是我所说的有帮助...
Files.delete(Path)
Throws:
NoSuchFileException
- if the file does not exist (optional specific exception)
DirectoryNotEmptyException
- if the file is a directory and could not otherwise be deleted because the directory is not empty (optional specific exception)
IOException
- if an I/O error occurs
同样,比 boolean
更有帮助。但请注意,还有 boolean deleteIfExists(Path)
,以防您希望非异常地处理那个微不足道的条件。由于所有其他重要条件仍作为异常处理,因此您不能混淆它们。
当然,API 设计者可以使用未经检查的异常,但这会导致什么结果呢?
That makes the use of the Stream here rather cumbersome as you need to surround it with a try/catch or rethrow the exception, which might not even be possible in legacy code.
没错。您将更改从未抛出此类异常的代码(因为 File.list()
returns null
在错误情况下)以调用可能抛出未经检查的异常的新方法,因为这“可能在遗留代码”——但遗留调用者并不期望该异常并且永远不会处理它。
捕获异常并像以前 null
被 returned 时一样表现(如果你曾经在旧代码中检查过),可能确实很麻烦,但这不是预期的方式处理这种情况,那为什么要让人舒服……
为什么 Files.list
会抛出 IOException
而 File.listFiles
不会?
查看Files.list
(Java 8) 的源代码,我更好奇为什么没有抛出一个UncheckedIOException
因为它也在迭代器中抛出。
如果我用 Files.list
替换 File.listFiles
-code,我现在需要处理一个异常,而我以前并没有真正处理过。
不用说,大多数开发人员甚至都不知道他们当时必须处理什么 ;-) 或者只是放一个 // TODO
and/or e.printStackTrace()
在那里。
这使得在此处使用 Stream
相当麻烦,因为您需要用 try/catch
包围它或重新抛出异常,这在遗留代码中甚至可能是不可能的。
那么为什么做出这个决定?
首先,开发者总是必须处理可能发生的错误情况。
-
Returns
null
if this abstract pathname does not denote a directory, or if an I/O error occurs. -
Throws:
NotDirectoryException
- if the file could not otherwise be opened because it is not a directory (optional specific exception)IOException
- if an I/O error occurs when opening the directory
所以不同之处在于,开发人员可能很容易忘记对 null
的检查,只要没有错误条件,就永远不会被注意到。当问题发生在客户身上并且您的应用程序抛出一个 NullPointerException
而不是处理一个可能微不足道的问题时,您将通过艰难的方式学习它。
您的陈述“不用说,大多数开发人员甚至不知道他们当时必须处理什么”,这很好地说明了这一点。确实如此,但编译器会在编译时告诉您,您必须处理 IOException
。与 File.list()
不同,检查 null
的失败可能会被忽略。在这两种情况中,你仍然可能处理得不好,但没有办法避免这种情况。
当然,一旦你明白你必须处理这个问题,你可能会问你想如何处理它,这取决于问题的种类。 return 值为 null
并不能说明问题所在。您可以通过 File.isDirectory()
检查“不是目录”条件并希望它在这期间没有改变,但是如果文件 是 一个目录,您不需要没有任何关于从哪里开始的提示。
相比之下,抛出的IOException
不仅可以让你区分NotDirectoryException
和其他错误情况,IOException
是一个森林的基础class可以准确描述问题的特定异常,例如AccessDeniedException
。即使异常有一个不明确的类型,它也可能有一个有意义的消息,你可以呈现给用户。
请注意,这是具有以下两个 API 的一般模式:
-
Returns:
true
if and only if the renaming succeeded;false
otherwise -
Returns:
true
if and only if the file or directory is successfully deleted;false
otherwise
那么当这些方法中的任何一个 returns false
时,你会怎么做?
Files.move(Path,Path,CopyOption...)
Throws:
…
FileAlreadyExistsException
- if the target file exists but cannot be replaced because theREPLACE_EXISTING
option is not specified (optional specific exception)DirectoryNotEmptyException
- theREPLACE_EXISTING
option is specified but the file cannot be replaced because it is a non-empty directory (optional specific exception)AtomicMoveNotSupportedException
- if the options array contains theATOMIC_MOVE
option but the file cannot be moved as an atomic file system operation.IOException
- if an I/O error occurs
这就是我所说的有帮助...
Files.delete(Path)
Throws:
NoSuchFileException
- if the file does not exist (optional specific exception)DirectoryNotEmptyException
- if the file is a directory and could not otherwise be deleted because the directory is not empty (optional specific exception)IOException
- if an I/O error occurs
同样,比 boolean
更有帮助。但请注意,还有 boolean deleteIfExists(Path)
,以防您希望非异常地处理那个微不足道的条件。由于所有其他重要条件仍作为异常处理,因此您不能混淆它们。
当然,API 设计者可以使用未经检查的异常,但这会导致什么结果呢?
That makes the use of the Stream here rather cumbersome as you need to surround it with a try/catch or rethrow the exception, which might not even be possible in legacy code.
没错。您将更改从未抛出此类异常的代码(因为 File.list()
returns null
在错误情况下)以调用可能抛出未经检查的异常的新方法,因为这“可能在遗留代码”——但遗留调用者并不期望该异常并且永远不会处理它。
捕获异常并像以前 null
被 returned 时一样表现(如果你曾经在旧代码中检查过),可能确实很麻烦,但这不是预期的方式处理这种情况,那为什么要让人舒服……