Что делает Java, если закрытие ресурса не удается?

В старые времена, когда мне приходилось использовать какой-то ресурс, я объявлял его вне блока try, создавал его в блоке try и закрывал в блоке finally. Для случая, если закрытие не удастся, оно было помещено во внутренний блок try.

Resource r=null;
try{
   r=new Resource();
   use(r); 
} 
catch(){
    outputAndLog(something);
}
finally{
    if(r != null){
        try{
        }
        catch(){
            outputAndLog(somethingElse);
        }
    }
}

Теперь мы можем использовать

try(Resource r = new Resource()){

Синтаксис Java. И блок, наконец, становится невидимым для нас. Но что в этом? Я имею в виду, что произойдет, если закрытие не удастся?

4 ответа

Ресурс: Ссылка


Прежде всего, try-with-resources на самом деле не обрабатывать Exceptions (как это делается в вашем коде путем регистрации). В основном это маршаллы возможно несколько Exceptions выброшенный из try-catch-finally построить в обрабатываемый объект - Exception содержащие подавлено Exceptions,

Если try блок бросает Exceptionресурс будет закрыт и в результате Exception будет подавлено. В результате брошенный Exception объект будет иметь ссылку на подавленный Exception,
Если try блок не бросает Exceptionресурс будет закрыт и в результате Exception будет переброшен

Таким образом, мы можем сделать вывод, что ваши примеры не эквивалентны:
Когда используешь try-catch-finally Exception вызванное закрытием после успешной обработки будет рассматриваться как "просто ошибка закрытия", в то время как с try-with-resources заявление такое Exception будет обрабатываться в равной степени Exception что произошло в фактической обработке.

ТЛ; др
Если обработка закрытия ресурсов включает в себя больше вещей, try-with-resources Заявление может привести к ошибкам, которые не встречаются со старым добрым try-catch-finally, Примером таких вещей является фиксация и откат jdbc подключение.


Итак, чтобы наконец-то произвести код, для

try(Resource resource = new Resource()){
    // other code
}

точный эквивалент будет:

{
    Resource resource = null;
    Exception exc = null;
    try {
        resource = new Resource();
        // other code
    } catch (Exception e) {
        exc = e;
        throw e;
    } finally {
        if (resource != null) {
            if (exc != null) {
                try {
                    resource.close();
                } catch (Throwable t) {
                    exc.addSuppressed(t);
                }
            } else {
                resource.close();
            }
        }
    }
}

Но что в этом?

try {
    Resource resource = initResource();
    Object possibleExceptionFromTry = null;
    ...
    if (stream != null) {
        if (possibleExceptionFromTry != null) {
            try {
                stream.close();
            } catch (Throwable exceptionFromClosing) {
                ((Throwable)possibleExceptionFromTry).addSuppressed(exceptionFromClosing);
            }
        } else {
            stream.close();
        }
    }
} catch (IOException exception) { ... }

Что произойдет, если закрытие не удастся?

Если все идет хорошо в try, вы поймаете исключение из Resource#close() в catch, В противном случае исключение из try будет подавлять исключение, выброшенное из Resource#close и положить его в массив, который может быть получен Throwable#getSuppressed:

} catch (IOException exception) {
    final Throwable[] suppressedExceptions = exception.getSuppressed();
    // exception = the exception from try
    // suppressedExceptions = the last exception (if any) is likely caused by closing
}

try-with-resources вызывает реализацию метода close от java.lang.AutoClosable интерфейс.

Из JLS 14.20.3

Ресурсы закрываются в порядке, обратном тому, в котором они были инициализированы. Ресурс закрывается, только если он инициализирован ненулевым значением. Исключение из закрытия одного ресурса не препятствует закрытию других ресурсов. Такое исключение подавляется, если исключение было сгенерировано ранее инициализатором, блоком try или закрытием ресурса.

Для вашего примера try(Resource r = new Resource()) должен быть этот полный эквивалент:

    Resource r = null;
    Exception ex = null;
    try {
        r = new Resource();
        use(r);
    } catch (Exception e){
        ex = e;
    }
    finally {
        if (r != null) {
            try {
                r.close();
            } catch (Exception e) {
                if (ex!=null){
                    ex.addSuppressed(e);
                } else {
                    ex = e;
                }
            }
        }
        if (ex != null){
            throw ex;
        }
    }

Согласно учебникам по Java:

Если исключение выдается из блока try, а одно или несколько исключений выбрасываются из оператора try-with-resources, то эти исключения, выбрасываемые из оператора try-with-resources, подавляются, и исключение, выбрасываемое блоком, является единственным это выброшено... Вы можете получить эти исключенные исключения, вызвав метод Throwable.getSuppressed из исключения, выданного блоком try.

Другие вопросы по тегам