Чи правильно я використовую Java 7 для спроби використання ресурсів


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;
}

Прочитавши ваше запитання ще раз, я не впевнений, чи добре його розумію. Ви можете пояснити це?
Марун

Привіт. Гепард, я намагаюся зрозуміти роль першого 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 або нарешті блок запускається після закриття оголошених ресурсів.


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 негативний. Хіба спроба з ресурсами не закриває всі AutoClosableресурси, незалежно від будь-яких винятків?
Prasoon Joshi

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.