Java 是否偏好嵌套的 try/catch 块?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/183499/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there a preference for nested try/catch blocks?
提问by eaolson
One of the things that always bugs me about using Readers and Streams in Java is that the close()
method can throw an exception. Since it's a good idea to put the close method in a finally block, that necessitates a bit of an awkward situation. I usually use this construction:
在 Java 中使用 Readers 和 Streams 时总是让我感到困扰的一件事是该close()
方法可能会抛出异常。由于将 close 方法放在 finally 块中是个好主意,因此需要一些尴尬的情况。我通常使用这种结构:
FileReader fr = new FileReader("SomeFile.txt");
try {
try {
fr.read();
} finally {
fr.close();
}
} catch(Exception e) {
// Do exception handling
}
But I've also seen this construction:
但我也看到过这种结构:
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
}
}
I prefer the first construction because there's only one catch block and it just seems more elegant. Is there a reason to actually prefer the second or an alternate construction?
我更喜欢第一种结构,因为只有一个 catch 块,而且看起来更优雅。是否有理由实际上更喜欢第二种或替代结构?
UPDATE: Would it make a difference if I pointed out that both read
and close
only throw IOExceptions? So it seems likely to me that, if read fails, close will fail for the same reason.
更新:将有所作为,如果我指出,无论read
与close
只引发IOException?所以在我看来,如果读取失败,关闭也会出于同样的原因失败。
采纳答案by Tom Hawtin - tackline
I would always go for the first example.
我总是选择第一个例子。
If close were to throw an exception (in practice that will never happen for a FileReader), wouldn't the standard way of handling that be to throw an exception appropriate to the caller? The close exception almost certainly trumps any problem you had using the resource. The second method is probably more appropriate if your idea of exception handling is to call System.err.println.
如果 close 要抛出异常(实际上,FileReader 永远不会发生这种情况),那么标准的处理方式不是抛出适合调用者的异常吗?close 异常几乎肯定会胜过您使用资源时遇到的任何问题。如果您对异常处理的想法是调用 System.err.println,则第二种方法可能更合适。
There is an issue of how far exceptions should be thrown. ThreadDeath should always be rethrown, but any exception within finally would stop that. Similarly Error should throw further than RuntimeException and RuntimeException further than checked exceptions. If you really wanted to you could write code to follow these rules, and then abstract it with the "execute around" idiom.
应该抛出多远的异常是一个问题。ThreadDeath 应该总是被重新抛出,但 finally 中的任何异常都会阻止它。类似地,Error 应该比 RuntimeException 和 RuntimeException 抛出得比检查异常更远。如果你真的想要,你可以编写代码来遵循这些规则,然后用“execute around”习语将其抽象化。
回答by Steve B.
The difference, as far as I can see, is that there are different exceptions and causes in play on different levels, and the
就我所见,不同之处在于在不同层面上有不同的例外和原因,并且
catch (Exception e)
捕获(例外 e)
obscures that. The only point of the multiple levels is to distinguish your exceptions, and what you'll do about them:
掩盖了这一点。多个级别的唯一要点是区分您的例外情况,以及您将如何处理它们:
try
{
try{
...
}
catch(IOException e)
{
..
}
}
catch(Exception e)
{
// we could read, but now something else is broken
...
}
回答by oxbow_lakes
I'm afraid there's a big problem with the first example, which is that if an exception happens on or after the read, the finally
block executes. So far so good. But what if the fr.close()
then causes another exception to be thrown? This will "trump" the first exception (a bit like putting return
in a finally
block) and you will lose all information about what actually caused the problemto begin with.
恐怕第一个例子有一个大问题,那就是如果在读取时或读取后发生异常,则finally
块会执行。到现在为止还挺好。但是如果fr.close()
then 导致另一个异常被抛出呢?这将“王牌”的第一个例外(有点像把return
一个finally
块)和你会失去什么实际造成问题的所有信息开始。
Your finally block should use:
你的 finally 块应该使用:
IOUtil.closeSilently(fr);
where this utility method just does:
这个实用方法的作用是:
public static void closeSilently(Closeable c) {
try { c.close(); } catch (Exception e) {}
}
回答by Michael Myers
I prefer the second one. Why? If both read()
and close()
throw exceptions, one of them could be lost. In the first construction, the exception from close()
overrides the exception from read()
, while in the second one, the exception from close()
is handled separately.
我更喜欢第二个。为什么?如果同时read()
和close()
抛出异常,其中一人可能会丢失。在第一个构造中,异常 fromclose()
覆盖了异常 from read()
,而在第二个构造中,异常 fromclose()
被单独处理。
As of Java 7, the try-with-resources constructmakes this much simpler.To read without caring about exceptions:
从 Java 7 开始,try-with-resources 构造使这变得更加简单。阅读而不关心异常:
try (FileReader fr = new FileReader("SomeFile.txt")) {
fr.read();
// no need to close since the try-with-resources statement closes it automatically
}
With exception handling:
带异常处理:
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]);
}
}
Only one try-catch is needed and all exceptions can be safely handled. You can still add a finally
block if you like, but there is no need to close the resources.
只需要一次 try-catch,所有异常都可以安全处理。finally
如果您愿意,您仍然可以添加块,但无需关闭资源。
回答by Martin York
The standard convention I use is that you must not let exceptions escape a finally block.
我使用的标准约定是您不能让异常逃脱 finally 块。
This is because if an exception is already propagating the exception thrown out of the finally block will trump the original exception (and thus be lost).
这是因为如果异常已经在传播,则从 finally 块中抛出的异常将胜过原始异常(从而丢失)。
In 99% of cases this is not what you want as the original exception is probably the source of your problem (any secondary exceptions may be side effects from the first but will obscure your ability to find the source of the original exception and thus the real problem).
在 99% 的情况下,这不是您想要的,因为原始异常可能是您的问题的根源(任何次要异常可能是第一个的副作用,但会掩盖您找到原始异常来源的能力,从而掩盖真正的异常问题)。
So your basic code should look like this:
所以你的基本代码应该是这样的:
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()
{}
}
回答by McDowell
If both readand closethrow an exception, the exception from readwill be hidden in option 1. So, the second option does more error handling.
如果read和close 都抛出异常,则read的异常将隐藏在选项 1 中。因此,第二个选项做了更多的错误处理。
However, in most cases, the first option will still be preferred.
但是,在大多数情况下,第一个选项仍然是首选。
- In many cases, you can't deal with exceptions in the method they are generated, but you still must encapsulate the stream handling within that operation.
- Try adding a writer to the code and see how verbose the second approach gets.
- 在许多情况下,您无法在生成它们的方法中处理异常,但您仍然必须在该操作中封装流处理。
- 尝试在代码中添加一个编写器,看看第二种方法有多冗长。
If you need to pass all the generated exceptions, it can be done.
如果您需要传递所有生成的异常,则可以完成。
回答by Scott Stanchfield
I usually do the following. First, define a template-method based class to deal with the try/catch mess
我通常会做以下事情。首先,定义一个基于模板方法的类来处理 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);
}
}
}
}
}
Note the "pending" exception -- this takes care of the case where an exception thrown during close would mask an exception we might really care about.
请注意“待处理”异常——这会处理关闭期间抛出的异常会掩盖我们可能真正关心的异常的情况。
The finally tries to close from the outside of any decorated stream first, so if you had a BufferedWriter wrapping a FileWriter, we try to close the BuffereredWriter first, and if that fails, still try to close the FileWriter itself.
finally 尝试首先从任何装饰流的外部关闭,因此如果您有一个包装 FileWriter 的 BufferedWriter,我们首先尝试关闭 BuffereredWriter,如果失败,仍然尝试关闭 FileWriter 本身。
You can use the above class as follows:
您可以按如下方式使用上述类:
try {
// ...
new AutoFileCloser() {
@Override protected void doWork() throws Throwable {
// declare variables for the readers and "watch" them
FileReader fileReader = null;
BufferedReader bufferedReader = null;
watch(fileReader = new FileReader("somefile"));
watch(bufferedReader = new BufferedReader(fileReader));
// ... do something with bufferedReader
// if you need more than one reader or writer
newFile(); // puts a flag in the
FileWriter fileWriter = null;
BufferedWriter bufferedWriter = null;
watch(fileWriter = new FileWriter("someOtherFile"));
watch(bufferedWriter = new BufferedWriter(fileWriter));
// ... do something with bufferedWriter
}
};
// .. other logic, maybe more AutoFileClosers
} catch (RuntimeException e) {
// report or log the exception
}
Using this approach you never have to worry about the try/catch/finally to deal with closing files again.
使用这种方法,您永远不必担心 try/catch/finally 再次处理关闭文件。
If this is too heavy for your use, at least think about following the try/catch and the "pending" variable approach it uses.
如果这对您来说太重了,至少考虑遵循 try/catch 和它使用的“挂起”变量方法。
回答by anjanb
2nd approach.
方法二。
Otherwise, I don't see you catching the exception from the FileReader constructor
否则,我看不到您从 FileReader 构造函数中捕获异常
http://java.sun.com/j2se/1.5.0/docs/api/java/io/FileReader.html#FileReader(java.lang.String)
http://java.sun.com/j2se/1.5.0/docs/api/java/io/FileReader.html#FileReader(java.lang.String)
public FileReader(String fileName) throws FileNotFoundException
public FileReader(String fileName) 抛出 FileNotFoundException
So, I usually have the constructor inside the try block as well. the finally block checks to see if the reader is NOT null before trying to do a close.
所以,我通常在 try 块中也有构造函数。finally 块在尝试关闭之前检查读取器是否为空。
The same pattern goes for Datasource, Connection, Statement, ResultSet.
相同的模式适用于数据源、连接、语句、结果集。
回答by Haoest
Sometimes nested try-catch is not a preference, consider this:
有时嵌套的 try-catch 不是首选,请考虑:
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{}
This is probably a bad example, but there are times you will need nested try-cactch.
这可能是一个不好的例子,但有时您需要嵌套的 try-cactch。
回答by Dunderklumpen
I like the approach by @Chris Marshall, but I never like to see exceptions getting swallowed silently. I think its best to log exceptions, especially if you are contiuing regardless.
我喜欢@Chris Marshall 的方法,但我从不喜欢看到异常被默默吞下。我认为最好记录异常,特别是如果您无论如何都要继续。
I always use a utility class to handle these sort of common exceptions, but I would make this tiny different to his answer.
我总是使用一个实用程序类来处理这些常见的异常,但我会让这个与他的答案略有不同。
I would always use a logger (log4j for me) to log errors etc.
我总是使用记录器(对我来说是 log4j)来记录错误等。
IOUtil.close(fr);
A slight modification to the utility method:
对实用程序方法稍作修改:
public static void close(Closeable c) {
try {
c.close();
} catch (Exception e) {
logger.error("An error occurred while closing. Continuing regardless", e);
}
}