бросает Exception в наконец-то блоки - PullRequest
98 голосов
/ 27 января 2009

Есть ли элегантный способ обработки исключений, которые выбрасываются в блок finally?

Например:

try {
  // Use the resource.
}
catch( Exception ex ) {
  // Problem with the resource.
}
finally {
   try{
     resource.close();
   }
   catch( Exception ex ) {
     // Could not close the resource?
   }
}

Как избежать использования try / catch в блоке finally?

Ответы [ 14 ]

71 голосов
/ 27 января 2009

Я обычно делаю это так:

try {
  // Use the resource.
} catch( Exception ex ) {
  // Problem with the resource.
} finally {
  // Put away the resource.
  closeQuietly( resource );
}

В другом месте:

protected void closeQuietly( Resource resource ) {
  try {
    if (resource != null) {
      resource.close();
    }
  } catch( Exception ex ) {
    log( "Exception during Resource.close()", ex );
  }
}
25 голосов
/ 27 января 2009

Я обычно использую один из closeQuietly методов в org.apache.commons.io.IOUtils:

public static void closeQuietly(OutputStream output) {
    try {
        if (output != null) {
            output.close();
        }
    } catch (IOException ioe) {
        // ignore
    }
}
22 голосов
/ 09 февраля 2012

Если вы используете Java 7, а resource реализует AutoClosable, вы можете сделать это (на примере InputStream):

try (InputStream resource = getInputStream()) {
  // Use the resource.
}
catch( Exception ex ) {
  // Problem with the resource.
}
8 голосов
/ 14 июня 2011

Возможно, немного чрезмерно, но может быть полезно, если вы позволяете всплыть исключениям, и вы ничего не можете записать в свой метод (например, потому что это библиотека, и вы бы предпочли, чтобы вызывающий код обрабатывал исключения и каротаж):

Resource resource = null;
boolean isSuccess = false;
try {
    resource = Resource.create();
    resource.use();
    // Following line will only run if nothing above threw an exception.
    isSuccess = true;
} finally {
    if (resource != null) {
        if (isSuccess) {
            // let close throw the exception so it isn't swallowed.
            resource.close();
        } else {
            try {
                resource.close();
            } catch (ResourceException ignore) {
                // Just swallow this one because you don't want it 
                // to replace the one that came first (thrown above).
            }
        }
    }
}

ОБНОВЛЕНИЕ: Я изучил это немного подробнее и нашел отличный пост в блоге от человека, который явно думал об этом больше, чем я: http://illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make-mess-of-stream.html Он делает еще один шаг и объединяет два исключения в одно, что Я мог бы быть полезным в некоторых случаях.

6 голосов
/ 09 апреля 2015

Начиная с Java 7 вам больше не нужно явно закрывать ресурсы в блоке finally , вместо этого вы можете использовать try -with-resources синтаксис. Оператор try-with-resources является оператором try, который объявляет один или несколько ресурсов. Ресурс - это объект, который должен быть закрыт после завершения программы. Оператор try-with-resources обеспечивает закрытие каждого ресурса в конце оператора. Любой объект, который реализует java.lang.AutoCloseable, который включает все объекты, которые реализуют java.io.Closeable, может использоваться в качестве ресурса.

Предположим, следующий код:

try( Connection con = null;
     Statement stmt = con.createStatement();
     Result rs= stmt.executeQuery(QUERY);)
{  
     count = rs.getInt(1);
}

Если произойдет какое-либо исключение, метод close будет вызван для каждого из этих трех ресурсов в обратном порядке, в котором они были созданы. Это означает, что метод close будет вызываться сначала для ResultSetm, затем для Statement и в конце для объекта Connection.

Также важно знать, что любые исключения, возникающие при автоматическом вызове методов close, подавляются. Эти исключенные исключения можно получить с помощью метода getsuppressed () , определенного в классе Throwable .

Источник: https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

3 голосов
/ 10 февраля 2012

Игнорирование исключений, возникающих в блоке 'finally', обычно является плохой идеей , если только вы не знаете, какими будут эти исключения и какие условия они будут представлять. В обычном шаблоне использования try/finally блок try переводит вещи в состояние, которого не ожидает внешний код, а блок finally восстанавливает состояние этих вещей до того, что ожидает внешний код. Внешний код, который перехватывает исключение, обычно ожидает, что, несмотря на исключение, все было восстановлено до состояния normal. Например, предположим, что некоторый код запускает транзакцию, а затем пытается добавить две записи; блок finally выполняет операцию «откат, если не зафиксировано». Вызывающий может быть подготовлен к возникновению исключения во время выполнения второй операции добавления и может ожидать, что, если он поймает такое исключение, база данных будет в том состоянии, в котором она находилась до того, как была предпринята любая операция. Однако, если во время отката возникает второе исключение, могут произойти плохие вещи, если вызывающая сторона делает какие-либо предположения о состоянии базы данных. Ошибка отката представляет собой крупный кризис - тот, который не должен быть обнаружен кодом, ожидающим простого исключения «Не удалось добавить запись».

Моя личная склонность заключалась бы в том, чтобы метод finally перехватывал возникающие исключения и заключал их в «CleanupFailedException», признавая, что такой сбой представляет собой серьезную проблему, и такое исключение не следует легко отлавливать.

2 голосов
/ 27 января 2009

Одно решение, если два исключения являются двумя разными классами

try {
    ...
    }
catch(package1.Exception err)
   {
    ...
   }
catch(package2.Exception err)
   {
   ...
   }
finally
  {
  }

Но иногда вы не можете избежать этой второй попытки. например для закрытия потока

InputStream in=null;
try
 {
 in= new FileInputStream("File.txt");
 (..)// do something that might throw an exception during the analysis of the file, e.g. a SQL error
 }
catch(SQLException err)
 {
 //handle exception
 }
finally
 {
 //at the end, we close the file
 if(in!=null) try { in.close();} catch(IOException err) { /* ignore */ }
 }
1 голос
/ 17 января 2012

После долгих размышлений я считаю следующий код лучше всего:

MyResource resource = null;
try {
    resource = new MyResource();
    resource.doSomethingFancy();
    resource.close(); 
    resource = null;  
} finally {
    closeQuietly(resource)
}

void closeQuietly(MyResource a) {
    if (a!=null)
        try {
             a.close();
        } catch (Exception e) {
             //ignore
        }
}

Этот код гарантирует следующее:

  1. Ресурс освобождается после завершения кода
  2. Исключения, возникающие при закрытии ресурса, не используются без их обработки.
  3. Код не пытается дважды закрыть ресурс, ненужное исключение не будет создано.
1 голос
/ 27 января 2009

Почему вы хотите избежать дополнительного блока? Поскольку блок finally содержит «нормальные» операции, которые могут вызвать исключение, и вы хотите, чтобы блок finally полностью выполнялся, вы ДОЛЖНЫ перехватывать исключения.

Если вы не ожидаете, что блок finally будет генерировать исключение, и вы все равно не знаете, как обработать исключение (вы просто сбросили бы трассировку стека), пусть исключение накапливается в стеке вызовов (удалите try-catch из блока finally).

Если вы хотите сократить объем ввода, вы можете реализовать «глобальный» внешний блок try-catch, который будет перехватывать все исключения, генерируемые в блоках finally:

try {
    try {
        ...
    } catch (Exception ex) {
        ...
    } finally {
        ...
    }

    try {
        ...
    } catch (Exception ex) {
        ...
    } finally {
        ...
    }

    try {
        ...
    } catch (Exception ex) {
        ...
    } finally {
        ...
    }
} catch (Exception ex) {
    ...
}
0 голосов
/ 15 декабря 2017

Изменение Resource с лучший ответ на Closeable

Streams реализует Closeable Таким образом, вы можете повторно использовать метод для всех потоков

protected void closeQuietly(Closeable resource) {
    if (resource == null) 
        return;
    try {
        resource.close();
    } catch (IOException e) {
        //log the exception
    }
}
...