Правильно ли я использую 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
Здравствуй. Cheetah, я пытаюсь понять роль первого catchвашего примера для Java 6. Т.е. catch (Exception ex) { throw ex; }- это просто повторное выбрасывание исключения, ничего не делает, его можно легко удалить, не повредив. Или я что-то упускаю?
Саша

Ответы:

103

Это правильно, и здесь нет требований к catchпункту. В Oracle java 7 doc говорится, что ресурс будет закрыт независимо от того, действительно ли создано исключение.

Вы должны использовать 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все равно закрывается. Вы можете найти более подробную информацию здесь и здесь .

Андрей Полунин
источник
Я пытаюсь понять, почему ресурс, созданный с помощью new FileReader(filePath)), не закрывается, если создается IllegalArgumentExceptionотрицательное sz. Не закрывает ли try-with-resources все AutoClosableресурсы независимо от каких-либо исключений?
Прасун Джоши
3
@PrasoonJoshi Нет, он вызывает только .close()те переменные, которые были объявлены в инициализаторе try-with-resources. Вот почему разделение его на два объявления в этом примере помогает.
Марио Карнейро,
4
Андрей и @Mario Вы и правы, и неправы. В первом примере FileReader не закрывается логикой try-with-resource. Но когда BufferedReader закрыт, он также закроет обернутый FileReader. Для доказательства взгляните на источник java.io.BufferedReader.close (). Как следствие, предпочтение следует отдавать коду из первого примера, поскольку он более краток.
jschreiner
7
@jschreiner Верно, хотя проблема Андрея (несколько надуманная), в которой sz < 0конструктор генерирует исключение, на самом деле вызывает утечку ресурса.
Марио Карнейро,
5
@mario Согласен. Внешний конструктор может выйти из строя, и произойдет утечка внутреннего ресурса. Я не видел этого раньше, спасибо.
jschreiner