Java try/catch/finally 获取/关闭资源时的最佳实践

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4092914/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 11:31:32  来源:igfitidea点击:

Java try/catch/finally best practices while acquiring/closing resources

javaresourcestry-catch

提问by Austin Hyde

While working on a school project, I wrote the following code:

在进行学校项目时,我编写了以下代码:

FileOutputStream fos;
ObjectOutputStream oos;
try {
    fos = new FileOutputStream(file);
    oos = new ObjectOutputStream(fos);

    oos.writeObject(shapes);
} catch (FileNotFoundException ex) {
    // complain to user
} catch (IOException ex) {
    // notify user
} finally {
    if (oos != null) oos.close();
    if (fos != null) fos.close();
}

The problem is that Netbeans is telling me the resource.close()lines throw an IOExceptionand therefore must either be caught or declared. It also is complaining that oosand fosmight not yet be initialized (despite the null checks).

问题是 Netbeans 告诉我这些resource.close()行抛出一个IOException,因此必须被捕获或声明。它还在抱怨oos并且fos可能尚未初始化(尽管进行了空检查)。

This seems a little strange, seeing as how the whole point is to stop the IOExceptionright there.

这似乎有点奇怪,因为重点是在IOException那里停下来。

My knee-jerk fix is to do this:

我的下意识修复是这样做:

} finally {
    try {
        if (oos != null) oos.close();
        if (fos != null) fos.close();
    } catch (IOException ex) { }
}

But deep down this bothers me and feels dirty.

但在内心深处,这让我感到困扰并感到肮脏。

I come from a C# background, where I would simply take advantage of a usingblock, so I am unsure of what the "right" way is to handle this.

我来自 C# 背景,在那里我只会利用一个using块,所以我不确定处理这个问题的“正确”方法是什么。

What isthe right way to handle this problem?

处理这个问题的正确方法什么?

采纳答案by Stephen C

If you are trying to catch and report all exceptions at source, a better solution is this:

如果您试图在源头捕获并报告所有异常,则更好的解决方案是:

ObjectOutputStream oos = null;
try {
   oos = new ObjectOutputStream(new FileOutputStream(file));
   oos.writeObject(shapes);
   oos.flush();
} catch (FileNotFoundException ex) {
    // complain to user
} catch (IOException ex) {
    // notify user
} finally {
    if (oos != null) {
        try {
            oos.close();
        } catch (IOException ex) {
            // ignore ... any significant errors should already have been
            // reported via an IOException from the final flush.
        }
    }
}

Notes:

笔记:

  • The standard Java wrapper streams, readers and writers all propagate closeand flushto their wrapped streams, etc. So you only need to close or flush the outermost wrapper.
  • The purpose of flushing explicitly at the end of the try block is so that the (real) handler for IOExceptiongets to see any write failures1.
  • When you do a close or flush on an output stream, there is a "once in a blue moon" chance that an exception will be thrown due to disc errors or file system full. You should not squash this exception!.
  • 标准的Java包装流,读者和作者都繁殖closeflush他们的包裹流等,所以你只需要关闭或刷新最外层的包装。
  • 在 try 块的末尾显式刷新的目的是让(真正的)处理程序IOException可以看到任何写入失败1
  • 当您对输出流执行关闭或刷新操作时,有可能由于磁盘错误或文件系统已满而引发异常。 你不应该压制这个例外!.

If you often have to "close a possibly null stream ignoring IOExceptions", then you could write yourself a helper method like this:

如果您经常不得不“关闭一个可能为空的流而忽略 IOExceptions”,那么您可以为自己编写一个辅助方法,如下所示:

public void closeQuietly(Closeable closeable) {
    if (closeable != null) {
        try {
            closeable.close();
        } catch (IOException ex) {
            // ignore
        }
    }
}

then you can replace the previous finally block with:

那么你可以用以下代码替换之前的 finally 块:

} finally {
    closeQuietly(oos);
}

(Another answer points out that a closeQuietlymethod is already available in an Apache Commons library ... if you don't mind adding a dependency to your project for a 10 line method. UPDATE: note that these methods are deprecated in version 2.6 of the API.)

(另一个答案指出closeQuietlyApache Commons 库中已经有一个方法可用……如果您不介意为 10 行方法的项目添加依赖项。 更新:请注意,这些方法在 2.6 版中已弃用API。)

But be careful that you only use closeQuietlyon streams where IO exceptions reallyare irrelevant.

但是请注意,您只能closeQuietly在 IO 异常确实无关紧要的流上使用。

1 - That is not necessary when using try-with-resources.

1 - 使用 try-with-resources 时不需要。



On the issue of flush()versus close()that people are asking about:

关于人们正在询问的flush()vs问题close()

  • The standard "filter" and "buffered" output streams and writers have an API contract that states that close()causes all buffered output to be flushed. You shouldfind that all other (standard) output classes that do output buffering will behave the same way. So, for a standard class it is redundant to call flush()immediately before close().
  • For custom and 3rd-party classes, you need to investigate (e.g. read the javadoc, look at the code), but any close()method that doesn't flush buffered data is arguably broken.
  • Finally, there is the issue of what flush()actually does. What the javadoc says is this (for OutputStream...)

    If the intended destination of this stream is an abstraction provided by the underlying operating system, for example a file, then flushing the stream guarantees only that bytes previously written to the stream are passed to the operating system for writing; it does not guarantee that they are actually written to a physical device such as a disk drive.

    So ... if you hope / imagine that calling flush()guarantees that your data will persist, you are wrong!(If you need to do that kind of thing, look at the FileChannel.forcemethod ...)

  • 标准的“过滤器”和“缓冲”输出流和编写器有一个 API 契约,声明close()导致所有缓冲输出被刷新。您应该会发现所有其他(标准)输出类进行输出缓冲的行为方式相同。因此,对于标准类,flush()close().
  • 对于自定义类和第 3 方类,您需要进行调查(例如阅读 javadoc,查看代码),但是任何close()不刷新缓冲数据的方法都可以说是被破坏的
  • 最后,还有flush()实际作用的问题。javadoc 所说的是这个(对于OutputStream...)

    如果此流的预期目标是底层操作系统提供的抽象,例如文件,则刷新流仅保证先前写入流的字节会传递给操作系统进行写入;它不保证它们确实被写入物理设备,例如磁盘驱动器。

    所以......如果你希望/想象调用flush()保证你的数据会持久化,那你就错了!(如果需要做那种事,看FileChannel.force方法吧……)



On the other hand, if you can use Java 7 or later, the "new" try-with-resources as described in @Mike Clark's answer is the best solution.

另一方面,如果您可以使用 Java 7 或更高版本,@Mike Clark 的回答中描述的“新”尝试资源是最佳解决方案。

If you are not using Java 7 or later for your new code, you are probably in a deep hole and digging deeper.

如果您的新代码没有使用 Java 7 或更高版本,那么您可能陷入了深渊并进行了更深入的挖掘。

回答by Aravind Yarram

Unfortunately there is no language level support. But there are lot of libraries out there that makes this simple. Check commons-io library. Or modern google-guava @ http://guava-libraries.googlecode.com/svn/trunk/javadoc/index.html

不幸的是,没有语言级别的支持。但是有很多库可以使这变得简单。检查 commons-io 库。或现代 google-guava @ http://guava-libraries.googlecode.com/svn/trunk/javadoc/index.html

回答by Mike

You're doing it right. It bothers the crap out of me too. You should initialize those streams to null explicitly - it's common convention. All you can do is join the club and want for using.

你做得对。它也困扰着我。您应该将这些流显式初始化为 null - 这是常见的约定。你所能做的就是加入俱乐部并想要using

回答by CurtainDog

Not a direct answer to your point but it is an unfortunate fact that because finallyand catchare both associated with trypeople think that they belong together. The best design for tryblocks is to either have a catchor a finallybut not both.

不是对您的观点的直接回答,但不幸的是,因为finallycatch都与try人们相关联,认为他们属于一起。try块的最佳设计是有一个catch或一个,finally但不能同时有。

In this case your comments hint that something is wrong. Why, in a method dealing with file IO, are we complaining about anything to the user. We might be running deep on a server somewhere with nary a user in sight.

在这种情况下,您的评论暗示出了问题。为什么,在处理文件 IO 的方法中,我们向用户抱怨什么。我们可能在某个地方的服务器上深入运行,看不到用户。

So, the code you present above should have a finallyto fail gracefully when things go wrong. It lacks the capacity to deal intelligently with errors however, so your catchbelongs somewhere higher up on the call chain.

因此,当出现问题时,您上面提供的代码应该可以finally优雅地失败。然而,它缺乏智能处理错误的能力,所以你catch属于调用链上更高的某个地方。

回答by maximdim

I usually have small class IOUtil with method such as:

我通常有小类 IOUtil 和方法,例如:

public static void close(Closeable c) {
    if (c != null) {
        try {
            c.close();
        }
        catch (IOException e) {
            // ignore or log
        }
    }
}

回答by RAY

How about this guys? No null check, no surprise. Everything is cleaned upon exit.

这些人呢?没有空检查,也就不足为奇了。一切都在退出时被清理干净。

try {
    final FileOutputStream fos = new FileOutputStream(file);
    try {
        final ObjectOutputStream oos = new ObjectOutputStream(fos);
        try {
            oos.writeObject(shapes);
            oos.flush();
        }
        catch(IOException ioe) {
            // notify user of important exception
        }
        finally {
            oos.close();
        }
    }
    finally {
        fos.close();
    }
}
catch (FileNotFoundException ex) {
    // complain to user
}
catch (IOException ex) {
    // notify user
}

回答by Mike Clark

Java 7 will add Automatic Resource Managementblocks. They are very similar to C#'s using.

Java 7 将添加自动资源管理块。它们与 C# 的using.

Josh Bloch wrote the technical proposal, which I highly recommend reading. Not just because it will give you a leg up on an upcoming Java 7 language feature, but because the specification motivates the need for such a construct, and in doing so, illustrates how to write correct code even in the absence of ARM.

Josh Bloch 撰写了技术提案,我强烈推荐阅读。不仅因为它会让你在即将到来的 Java 7 语言特性上有优势,而且因为规范激发了对这种结构的需求,并且这样做说明了即使在没有 ARM 的情况下如何编写正确的代码。

Here's an example of the Asker's code, translated into ARM form:

下面是一个 Asker 代码的例子,翻译成 ARM 形式:

try (FileOutputStream fos = new FileOutputStream(file);
        ObjectOutputStream oos = new ObjectOutputStream(fos)) 
{
    oos.writeObject(shapes);
}
catch (FileNotFoundException ex) 
{
    // handle the file not being found
}
catch (IOException ex) 
{
    // handle some I/O problem
}

回答by Phil

Current best practice for try/catch/finally involving objects that are closeable (e.g. Files) is to use Java 7's try-with-resource statement, e.g.:

当前涉及可关闭对象(例如文件)的 try/catch/finally 的最佳实践是使用 Java 7 的 try-with-resource 语句,例如:

try (FileReader reader = new FileReader("ex.txt")) {
    System.out.println((char)reader.read());
} catch (IOException ioe) {
    ioe.printStackTrace();
}

In this case, the FileReader is automatically closed at the end of the try statement, without the need to close it in an explicit finally block. There are a few examples here:

在这种情况下,FileReader 在 try 语句结束时自动关闭,无需在显式 finally 块中关闭它。这里有几个例子:

http://ppkwok.blogspot.com/2012/11/java-cafe-2-try-with-resources.html

http://ppkwok.blogspot.com/2012/11/java-cafe-2-try-with-resources.html

The official Java description is at:

官方 Java 描述位于:

http://docs.oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html

http://docs.oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html