Что делает 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;
}
}
Если исключение выдается из блока try, а одно или несколько исключений выбрасываются из оператора try-with-resources, то эти исключения, выбрасываемые из оператора try-with-resources, подавляются, и исключение, выбрасываемое блоком, является единственным это выброшено... Вы можете получить эти исключенные исключения, вызвав метод Throwable.getSuppressed из исключения, выданного блоком try.