我们与他进行了一些详细的讨论,他的意思是,如果我确切地知道会发生什么,我应该尝试处理它,而不是诉诸 try/catch 后备。
如果你的导师告诉你应该测试以避免 IO 异常,那么我担心他是完全错误的。
大多数 I/O 操作都被声明为抛出IOException
,这是一个检查异常。当您调用这些方法时,您的代码必须处理或传播异常。你没有选择。Java 语言对所有检查的异常都要求这样做。
测试所有可能的 IOExceptions 是不切实际的。例如,此代码可能会失败:
if (f.exists() && f.canRead()) {
os = new FileInputStream(f);
}
为什么?因为构造函数可能会抛出 IOException 的原因还有其他,例如硬盘错误、网络错误(如果文件在远程挂载的文件系统上)、强制访问控制失败等。除了尝试打开文件之外,通常无法以任何其他方式检测潜在条件。
然后是竞争条件的问题。在上面的示例中,在测试文件可读性和尝试打开它之间有一个小的时间窗口。在那个时间窗口内,有可能一些其他线程,或者一些外部进程会删除文件或改变它的访问权限,从而导致打开失败。没有办法关闭那个时间窗口。
然后是为什么你甚至想要避免异常的问题。我怀疑你的老师是“例外否认者”;即,将“例外只用于例外的事情”的建议推向不合逻辑的极端的人之一。
“异常应该只用于特殊的事情”的建议基本上是说你不应该过度使用异常,因为它们有点昂贵。但贵是相对的。
在这个例子中,对File.exists()
和对的调用File.canRead()
也很昂贵,因为它们都需要一个系统调用,这将花费数千个时钟周期。更糟糕的是,除非你的应用程序有什么不寻常的地方,否则这两个测试将会成功……所以你刚刚做了两个不必要的系统调用来避免无论如何都不会抛出的异常。除非这些测试失败的可能性很高(即 > 50%),否则跳过测试、尝试打开并在发生异常时处理异常会更有效。
我不会给我的教授贴上这样的标签。我们没有专门讨论 I/O 的复杂性,更多的是笼统地说,如果我能避免它,我应该避免它。我不知道(他也没有详细说明)有一些非常不可避免的例外情况。
好的。所以你的教授没有这么说。对他有好处。
是的,它们在不止一种意义上是不可避免的。
至于拒绝使用 .exists() 检查,对性能的影响有那么大吗?
是的。看到这个 -系统调用开销
或者如果你不相信,写一个微基准并测量它。并将其与抛出和捕获异常的开销进行比较。
会是桌面与移动设备之类的问题吗?
没有。或者对于操作系统使用虚拟内存来阻止一个“应用程序”干扰另一个“应用程序”的移动设备来说当然不是。
为故障排除/调试(如果我可以更好地查明问题同时避免程序崩溃)的清晰度进行权衡是否值得?
好吧,我认为这段代码:
try (InputStream is = new FileInputStream(f)) {
// use file
} catch (IOException ex) {
System.err.println("IO error: ": ex.getMessage());
}
...比这更简单,更容易维护和调试:
if (!f.exists()) {
System.err.println("File " + f + " does not exist");
} else if (!f.isDirectory()) {
System.err.println("File " + f + " is a directory");
} else if (!f.canRead()) {
System.err.println("File " + f + " is not readable");
} ...
} else {
try (InputStream is = new FileInputStream(f)) {
// read
} catch (IOException ex) {
System.err.println("IO error: "+ ex.getMessage());
}
}
你不同意吗?