Java 是否有嵌套try/catch块的首选项?
在Java中使用读卡器和流时,我经常遇到的一个问题是Java 是否有嵌套try/catch块的首选项?,java,try-catch,Java,Try Catch,在Java中使用读卡器和流时,我经常遇到的一个问题是close()方法会引发异常。由于将close方法放在finally块中是一个好主意,因此需要一点尴尬的情况。我通常使用这种结构: FileReader fr = new FileReader("SomeFile.txt"); try { try { fr.read(); } finally { fr.close(); } } catch(Exception e) { // Do
close()
方法会引发异常。由于将close方法放在finally块中是一个好主意,因此需要一点尴尬的情况。我通常使用这种结构:
FileReader fr = new FileReader("SomeFile.txt");
try {
try {
fr.read();
} finally {
fr.close();
}
} catch(Exception e) {
// Do exception handling
}
FileReader fr = new FileReader("SomeFile.txt");
try {
fr.read()
} catch (Exception e) {
// Do exception handling
} finally {
try {
fr.close();
} catch (Exception e) {
// Do exception handling
}
}
但我也看到了这种结构:
FileReader fr = new FileReader("SomeFile.txt");
try {
try {
fr.read();
} finally {
fr.close();
}
} catch(Exception e) {
// Do exception handling
}
FileReader fr = new FileReader("SomeFile.txt");
try {
fr.read()
} catch (Exception e) {
// Do exception handling
} finally {
try {
fr.close();
} catch (Exception e) {
// Do exception handling
}
}
我更喜欢第一个建筑,因为只有一个挡块,看起来更优雅。是否有理由选择第二种或另一种结构
更新:如果我指出
read
和close
都只抛出IOException,会有什么不同吗?因此,在我看来,如果读取失败,close也会因为同样的原因失败。据我所知,不同的是,在不同的级别上存在不同的异常和原因,并且
捕获(例外e)
掩盖了这一点。多个级别的唯一目的是区分您的异常,以及您将如何处理它们:
try
{
try{
...
}
catch(IOException e)
{
..
}
}
catch(Exception e)
{
// we could read, but now something else is broken
...
}
恐怕第一个示例存在一个大问题,即如果在读取时或读取后发生异常,
最后执行块。到现在为止,一直都还不错。但是如果fr.close()
导致抛出另一个异常,该怎么办?这将“压倒”第一个异常(有点像将return
放在finally
块中),并且您将丢失有关问题实际原因的所有信息
您的finally块应使用:
IOUtil.closeSilently(fr);
此实用程序方法仅用于:
public static void closeSilently(Closeable c) {
try { c.close(); } catch (Exception e) {}
}
我更喜欢第二个。为什么?如果read()
和close()
都抛出异常,其中一个可能会丢失。在第一种构造中,来自close()
的异常覆盖来自read()
的异常,而在第二种构造中,来自close()
的异常被单独处理
从Java 7开始,这就简单多了。阅读时不必考虑异常:
try (FileReader fr = new FileReader("SomeFile.txt")) {
fr.read();
// no need to close since the try-with-resources statement closes it automatically
}
对于异常处理:
try (FileReader fr = new FileReader("SomeFile.txt")) {
fr.read();
// no need to close since the try-with-resources statement closes it automatically
} catch (IOException e) {
// Do exception handling
log(e);
// If this catch block is run, the FileReader has already been closed.
// The exception could have come from either read() or close();
// if both threw exceptions (or if multiple resources were used and had to be closed)
// then only one exception is thrown and the others are suppressed
// but can still be retrieved:
Throwable[] suppressed = e.getSuppressed(); // can be an empty array
for (Throwable t : suppressed) {
log(suppressed[t]);
}
}
只需要一个try-catch,所有异常都可以安全处理。如果愿意,您仍然可以添加finally
块,但无需关闭资源。我使用的标准约定是,您不能让异常逃逸finally块
这是因为如果一个异常已经在传播,那么从finally块抛出的异常将超过原始异常(并因此丢失)
在99%的情况下,这不是您想要的,因为原始异常可能是问题的根源(任何次要异常都可能是第一个异常的副作用,但会模糊您找到原始异常来源的能力,从而导致真正的问题)
因此,您的基本代码应该如下所示:
try
{
// Code
}
// Exception handling
finally
{
// Exception handling that is garanteed not to throw.
try
{
// Exception handling that may throw.
}
// Optional Exception handling that should not throw
finally()
{}
}
我总是选择第一个例子
如果close抛出一个异常(实际上,对于FileReader来说,这是永远不会发生的),那么标准的处理方法不是抛出一个适合调用方的异常吗?几乎可以肯定,close异常胜过了您在使用资源时遇到的任何问题。如果异常处理的想法是调用System.err.println,那么第二种方法可能更合适
有一个问题是异常应该抛出多远。ThreadDeath应该总是被重新调用,但是finally中的任何异常都会阻止它。类似地,错误应该抛出比RuntimeException更远的异常,RuntimeException应该抛出比已检查异常更远的异常。如果您真的想这样做,您可以编写遵循这些规则的代码,然后使用“围绕执行”成语对其进行抽象。如果read和close都抛出异常,那么read的异常将隐藏在选项1中。因此,第二个选项执行更多的错误处理
然而,在大多数情况下,第一种选择仍然是首选
在许多情况下,您无法在生成异常的方法中处理异常,但仍然必须在该操作中封装流处理
尝试向代码中添加一个编写器,看看第二种方法有多冗长
如果需要传递所有生成的异常,我通常会执行以下操作。首先,定义一个基于模板方法的类来处理try/catch混乱
import java.io.Closeable;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
public abstract class AutoFileCloser {
private static final Closeable NEW_FILE = new Closeable() {
public void close() throws IOException {
// do nothing
}
};
// the core action code that the implementer wants to run
protected abstract void doWork() throws Throwable;
// track a list of closeable thingies to close when finished
private List<Closeable> closeables_ = new LinkedList<Closeable>();
// mark a new file
protected void newFile() {
closeables_.add(0, NEW_FILE);
}
// give the implementer a way to track things to close
// assumes this is called in order for nested closeables,
// inner-most to outer-most
protected void watch(Closeable closeable) {
closeables_.add(0, closeable);
}
public AutoFileCloser() {
// a variable to track a "meaningful" exception, in case
// a close() throws an exception
Throwable pending = null;
try {
doWork(); // do the real work
} catch (Throwable throwable) {
pending = throwable;
} finally {
// close the watched streams
boolean skip = false;
for (Closeable closeable : closeables_) {
if (closeable == NEW_FILE) {
skip = false;
} else if (!skip && closeable != null) {
try {
closeable.close();
// don't try to re-close nested closeables
skip = true;
} catch (Throwable throwable) {
if (pending == null) {
pending = throwable;
}
}
}
}
// if we had a pending exception, rethrow it
// this is necessary b/c the close can throw an
// exception, which would remove the pending
// status of any exception thrown in the try block
if (pending != null) {
if (pending instanceof RuntimeException) {
throw (RuntimeException) pending;
} else {
throw new RuntimeException(pending);
}
}
}
}
}
使用这种方法,您不必担心try/catch/finally会再次关闭文件
如果这对于您的使用来说太重了,至少考虑遵循try/catch和它使用的“挂起”变量方法。2nd方法
否则,我看不到您从FileReader构造函数捕获异常
公共文件读取器(字符串文件名)
抛出FileNotFoundException
因此,我通常在try块中也有构造函数。在尝试关闭之前,finally块检查读取器是否为null
数据源、连接、语句、结果集的模式相同。
< P>有时嵌套Testcatch不是首选项,考虑如下:
try{
string s = File.Open("myfile").ReadToEnd(); // my file has a bunch of numbers
// I want to get a total of the numbers
int total = 0;
foreach(string line in s.split("\r\n")){
try{
total += int.Parse(line);
} catch{}
}
catch{}
这可能是一个不好的例子,但有时您需要嵌套的try cacch。我喜欢@Chris Marshall的方法,但我从不喜欢看到异常被默默吞没。我认为最好是记录异常,特别是如果你继续不管
我总是使用一个实用类来处理这类常见的异常,但我会让这一点与他的答案有所不同
我总是使用一个记录器(log4j为我)来记录错误等
IOUtil.close(fr);
对实用方法稍作修改:
public static void close(Closeable c) {
try {
c.close();
} catch (Exception e) {
logger.error("An error occurred while closing. Continuing regardless", e);
}
}
在某些情况下,嵌套的Try-Catch是不可避免的。例如,当错误恢复代码本身可以引发错误和异常时。但是为了提高代码的可读性,您总是可以将嵌套块提取到它自己的方法中。查看博客文章,了解更多关于嵌套Try-Catch-Finally块的示例。请告诉我。注意: