首先要做的是尝试查找有关用于读取文本和 XML 文件的 API 的文档。 Javadoc 是记录 Java API 的内容,它将自动记录方法声明的所有异常。在大多数情况下,捕获这些异常就足够了。此外,请确保所有方法参数都正确,以免方法意外抛出 NullPointerException、IllegalArgumentException 或类似的东西。
如果您不了解已检查和未检查的异常,请立即阅读相关内容(例如 Checked vs Unchecked Exception)。简而言之,如果没有在throws 子句中声明,方法就不能抛出已检查异常,并且方法调用者必须要么捕获并处理异常,要么声明它抛出异常。没有throws 子句的方法只能抛出扩展RuntimeException 的异常,尽管有时程序员也会声明和/或记录这些异常。
确保您了解不同类型的异常。任何可投掷的东西都扩展Throwable(参见Throwable Javadoc)。 Error 和 Exception 扩展可投掷。 Error throwables 通常是你不能做太多的事情,比如OutOfMemoryError,所以通常忽略这些是安全的。 Exceptions 是您所关心的。 RuntimeException 扩展 Exception,运行时异常未选中。任何其他扩展 Exception 的内容都被选中。
IO 操作一般会抛出IOException、FileNotFoundException 和SecurityException。 IOException 被检查,并且由于 FileNotFoundException 扩展 IOException,它也被检查(仅捕获 IOException 将处理两者)。它们可以单独处理,但需要在IOException 之前捕获FileNotFoundException。 SecurityException 未选中,除非代码在 JVM 中运行,使用安全管理器来限制程序,否则您不太可能看到它(文件权限拒绝访问可能会抛出 IOException)。
如果您不确定您的代码是否能够捕获所有异常并正确处理它们,那么您始终可以捕获 Exception 并进行处理。或者,您可以捕获Throwable,但您可能会忽略程序无法处理的错误,并且不会优雅地崩溃。如果您确实捕获了Throwable,那么最好使用它来捕获详细信息并优雅地崩溃,例如日志记录或 GUI 应用程序试图在从屏幕上消失之前显示错误。
最后,制定处理异常的计划。我经常看到的一个错误是捕获异常、打印和错误,然后由于 IO 资源未初始化而继续崩溃。如果方法不能完全恢复或正常崩溃,让它抛出异常。不要忘记您可以将信息附加到异常并使用异常链接,以便被调用者可以打印更有意义的错误。
例如,假设您的程序可以读取一些设置文件并逐行处理它。以下方法假定参数 fileName 已被检查,尽管您应该在 public 方法上显式检查参数并相应地抛出 NullPointerException 或 IllegalArgumentException。
/**
* Read a settings file and configure this object.
*
* @param fileName Name of the file. Must not be <code>null</code>
* or empty.
*
* @throws IOException If an IO error occurs while opening or
* reading the file.
* @throws SecurityException If a security manager exists and its
* <code>checkRead()</code> method denies read access to the file.
*/
private void loadSettingsFile(String fileName)
throws IOException, SecurityException {
String line; // Line read from file
// Read file
try (BufferedReader reader = new BufferedReader(new FileReader(new File(fileName)))) {
while ((line = reader.readLine()) != null) {
line = line.trim();
// Do something with line
}
} catch (IOException ex) {
throw new IOException("IO error reading settings file: " + ex.getMessage(), ex);
} catch (SecurityException ex) {
throw new SecurityException("Security error reading settings file: " + ex.getMessage(), ex);
} catch (Exception ex) {
throw new IOException(String.format("Unknown error reading settings file: %s: %s", fileName, ex.getMessage()), ex);
}
return;
}
在此示例中,SecurityException 和 IOException(也捕获 FileNotFoundException)被捕获,并生成了一个具有附加信息的相同类型的新异常,例如抛出异常时程序正在执行的操作.在新异常(构造函数的第二个参数)中包含原始异常会保留完整的堆栈跟踪(这是异常链接)。我知道当IOException和SecurityException在这段代码中抛出时,包含文件名;我不需要再次将其添加到异常中。当抛出其他异常时,它可能不会被包含在内,所以我在捕获Exception时明确这样做。
注意FileReader() 没有声明也没有记录SecurityException,但它确实抛出了它(在 OpenJDK 1.7.0_91 中验证)。这又回到了关于如何捕获所有异常的问题。有时它们没有记录,您必须根据经验进行猜测。从您不知道或不信任的代码中捕获Exception 也是一种选择。
在这个例子中,我选择将Exception 重新抛出为IOException,因为我的被调用者已经在处理这些。就风格而言,我更喜欢记录所有异常(甚至是未经检查的异常),所以这个判断调用对我来说是有意义的。