Броски Исключение в блоках finally

Есть ли элегантный способ обработки исключений, которые выбрасываются в блок 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?

Ответ 1

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

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

Ответ 2

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

public static void closeQuietly(OutputStream output) {
    try {
        if (output != null) {
            output.close();
        }
    } catch (IOException ioe) {
        // ignore
    }
}

Ответ 3

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

try (InputStream resource = getInputStream()) {
  // Use the resource.
}
catch( Exception ex ) {
  // Problem with the resource.
}

Ответ 4

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

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).
            }
        }
    }
}

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

Ответ 5

Начиная с 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 сначала будет вызываться для ResultSetm, а затем Statement и в конце для объекта Connection.

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

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

Ответ 6

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

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

Ответ 7

Одно решение, если два Исключения представляют собой два разных класса

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

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

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 */ }
 }

Ответ 8

Почему вы хотите избежать дополнительного блока? Поскольку блок finally содержит "нормальные" операции, которые могут генерировать исключение, и вы хотите, чтобы блок finally полностью выполнялся, вы должны перехватывать исключения.

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

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

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

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

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

Ответ 9

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

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

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

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

Ответ 10

Если вы можете попробовать, чтобы избежать возникновения ошибки.

try{...}
catch(NullArgumentException nae){...}
finally
{
  //or if resource had some useful function that tells you its open use that
  if (resource != null) 
  {
      resource.Close();
      resource = null;//just to be explicit about it was closed
  }
}

Кроме того, вы должны, вероятно, только перехватывать исключения, из которых вы можете восстановить, если вы не можете их восстановить, то пусть он распространяется на верхний уровень вашей программы. Если вы не можете проверить условие ошибки, что вам придется окружить ваш код блоком try catch, как вы уже сделали (хотя я бы рекомендовал все еще улавливать определенные ожидаемые ошибки).

Ответ 11

Вы можете реорганизовать это в другой метод...

public void RealDoSuff()
{
   try
   { DoStuff(); }
   catch
   { // resource.close failed or something really weird is going on 
     // like an OutOfMemoryException 
   }
}

private void DoStuff() 
{
  try 
  {}
  catch
  {
  }
  finally 
  {
    if (resource != null) 
    {
      resource.close(); 
    }
  }
}

Ответ 12

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

MyResource r = null;
try { 
   // use resource
} finally {   
    if( r != null ) try { 
        r.close(); 
    } catch( ThatSpecificExceptionOnClose teoc ){}
}

Обоснование: если я закончил с ресурсом, и единственная проблема, с которой я сталкиваюсь, это его закрытие, я не могу с этим поделать. Это не имеет смысла либо убить весь поток, если я все равно с ресурсом.

Это один из случаев, когда, по крайней мере, для меня, безопасно игнорировать это проверенное исключение.

До сих пор у меня не было проблем с использованием этой идиомы.

Ответ 13

try {
    final Resource resource = acquire();
    try {
        use(resource);
    } finally {
        resource.release();
    }
} catch (ResourceException exx) {
    ... sensible code ...
}

Работа выполнена. Нет нулевых тестов. Одиночный улов, включают в себя получение и освобождение исключений. Конечно, вы можете использовать идиому Execute Around, и ее нужно написать только один раз для каждого типа ресурса.

Ответ 14

Изменение Resource от лучшего ответа до Closeable p >

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

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