我是否正确使用Java 7 try-with-resources


87

如果引发异常,我希望缓冲的读取器和文件读取器关闭并释放资源。

public static Object[] fromFile(String filePath) throws FileNotFoundException, IOException
{
    try (BufferedReader br = new BufferedReader(new FileReader(filePath)))
    {
        return read(br);
    } 
}

但是,是否需要有一个catch成功关闭的子句?

编辑:

本质上,Java 7中的上述代码是否等同于Java 6的以下代码:

public static Object[] fromFile(String filePath) throws FileNotFoundException, IOException
{

    BufferedReader br = null;

    try
    {
        br = new BufferedReader(new FileReader(filePath));

        return read(br);
    }
    catch (Exception ex)
    {
        throw ex;
    }
    finally
    {
        try
        {
            if (br != null) br.close();
        }
        catch(Exception ex)
        {
        }
    }

    return null;
}

再次阅读您的问题后,不确定我是否理解得很好。你能解释一下吗?
Maroun

你好 猎豹,我试图了解catch您的Java 6示例第一个的作用。即catch (Exception ex) { throw ex; },它只是抛出异常,它什么也没做,可以轻松删除而不会造成任何伤害。还是我错过了什么?
萨沙(Sasha)

Answers:


103

是正确的,没有catch子句的要求。Oracle Java 7文档说,无论是否实际引发异常,资源都将被关闭。

catch仅当您想对异常做出反应时,才应使用子句。该catch子句将在资源关闭执行。

这是Oracle教程的摘录:

下面的示例从文件中读取第一行。它使用BufferedReader的实例从文件中读取数据。BufferedReader是一种资源,程序完成后必须将其关闭:

static String readFirstLineFromFile(String path) throws IOException {
    try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
} // In this example, the resource declared in the try-with-resources statement is a BufferedReader.

...因为BufferedReader实例是在try-with-resource语句中声明的,所以无论try语句是正常完成还是突然完成(由于BufferedReader.readLine方法抛出IOException),它都将关闭。

编辑

关于新编辑的问题:

Java 6中的代码执行catch和之后的代码finally块。这导致资源仍可能在catch块中打开。

在Java 7语法中,资源在块之前关闭catch,因此资源在catch块执行期间已经关闭。在上面的链接中对此进行了说明:

在try-with-resources语句中,在声明的资源已关闭之后,将运行任何catch或finally块。


69

在这种特殊情况下,您可以使用try-with-resources正常工作,但总体上并不完全正确。您不应该这样链接资源,因为它可能导致不愉快的意外。假设您具有可变的缓冲区大小:

public static Object[] fromFile(String filePath) throws FileNotFoundException, IOException
{
    int sz = /* get buffer size somehow */
    try (BufferedReader br = new BufferedReader(new FileReader(filePath), sz))
    {
        return read(br);
    } 
}

假设出了点问题,您最终sz被否定了。在这种情况下,您的文件资源(通过创建new FileReader(filePath))将不会关闭。

为避免此问题,您应像这样分别指定每个资源:

public static Object[] fromFile(String filePath) throws FileNotFoundException, IOException
{
    int sz = /* get buffer size somehow */
    try (FileReader file = new FileReader(filePath);
         BufferedReader br = new BufferedReader(file, sz))
    {
        return read(br);
    } 
}

在这种情况下,即使br失败的初始化file仍然会关闭。您可以在此处此处找到更多详细信息。


我试图了解为什么在sz为负数时抛出new FileReader(filePath))anIllegalArgumentException时无法通过创建的资源关闭。AutoClosable不管抛出任何异常,try-with-resources是否都不会关闭所有资源?
Prasoon Joshi 2015年

3
@PrasoonJoshi不,它仅调用.close()在try-with-resources初始化程序中声明的变量。这就是为什么在此示例中将其分为两个声明的原因。
Mario Carneiro

4
Andrii和@Mario你是对是错。在第一个示例中,try-with-resource逻辑未关闭FileReader。但是,当BufferedReader关闭时,它也将关闭包装的FileReader。为了证明这一点,请查看java.io.BufferedReader.close()的源代码。因此,应该首选第一个示例中的代码,因为它更简洁。
jschreiner '16

7
@jschreiner是的,尽管Andrii的(有些人为的)sz < 0导致构造函数抛出异常的问题实际上会导致资源泄漏。
Mario Carneiro

5
@mario我同意。外部构造函数可能会失败,而内部资源则会泄漏。我以前没看到过,谢谢。
jschreiner '16
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.