Pergunta

Existe uma maneira elegante de lidar com exceções que são lançadas no bloco finally?

Por exemplo:

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

Como evitar a try / catch no bloco finally?

Foi útil?

Solução

Eu costumo fazê-lo como este:

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

Em outra parte:

protected void closeQuietly( Resource resource ) {
  try {
    if (resource != null) {
      resource.close();
    }
  } catch( Exception ex ) {
    log( "Exception during Resource.close()", ex );
  }
}

Outras dicas

Eu normalmente uso um dos métodos closeQuietly em org.apache.commons.io.IOUtils:

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

Se você estiver usando Java 7 e implementos resource AutoClosable, você pode fazer isso (usando InputStream como um exemplo):

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

Provavelmente um pouco mais do topo, mas talvez útil se você está deixando exceções bolha e você pode não log qualquer coisa de dentro de seu método (por exemplo, porque é uma biblioteca e você preferir deixar as chamadas exceções identificador de código e logging):

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: Eu olhei para isso um pouco mais e encontrou um grande post de alguém que tenha claramente pensado nisso mais do que eu: http://illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make-mess-of -stream.html Ele vai um passo além e combina as duas exceções em um, que eu podia ver ser útil em alguns casos.

A partir do Java 7, você não precisa mais recursos fechar explicitamente em um finalmente bloco em vez disso você pode usar tente sintaxe -com-recursos. A instrução try-with-recursos é uma instrução try que declara um ou mais recursos. Um recurso é um objeto que deve ser fechada após o programa é terminado com ele. Os garante instrução try-com-recursos que cada recurso é fechada no final da declaração. Qualquer objeto que implementa java.lang.AutoCloseable, que inclui todos os objetos que implementam java.io.Closeable, pode ser usado como um recurso.

Suponha o seguinte código:

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

Se qualquer exceção acontece o Fechar método será chamado em cada um desses três recursos na ordem inversa em que foram criados. Isso significa que o método close seria chamado pela primeira vez para ResultSetm então a Declaração e no final para o objeto de conexão.

Também é importante saber que todas as exceções que ocorrem quando os métodos de perto é chamado automaticamente são suprimidos. Estas excepções suprimidos podem ser recuperados por getsuppressed () método definido na Throwable classe.

Fonte: https://docs.oracle.com/javase/ tutorial / essencial / exceções / tryResourceClose.html

Ignorando exceções que ocorrem em um bloco 'finalmente' é geralmente um má idéia , a menos que se saiba o que essas exceções serão e quais as condições que irão representar. No padrão de uso try/finally normal, o bloco try lugares coisas em um estado do código fora não vai estar esperando, e o bloco finally restaura o estado dessas coisas para o que o espera de código fora. código fora que captura uma exceção geralmente esperar que, não obstante a excepção, tudo foi restaurado para um estado normal. Por exemplo, suponha que algum código inicia uma transação e, em seguida, tenta adicionar dois registros; os "finalmente" executa bloco a "reversão se não for cometido" a operação. Um chamador pode ser preparado para uma exceção para ocorrer durante a execução da segunda "adicionar" operação, e pode esperar que, se ele pega essa excepção, o banco de dados estará no estado em que estava antes de qualquer operação foi tentada. Se, no entanto, ocorre uma segunda exceção durante a reversão, coisas ruins poderiam acontecer se o chamador faz quaisquer suposições sobre o estado de banco de dados. O fracasso reversão representa um principal crise -. Um que não deve ser pego de código esperando um mero "Falha ao adicionar registro" exceção

A minha inclinação pessoal seria ter um finally exceções método de captura que ocorrem e envolvê-los em uma "CleanupFailedException", reconhecendo que tal falha representa um grande problema e tal excepção não deve ser pego de ânimo leve.

Uma solução, se as duas exceções são duas classes diferentes

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

Mas às vezes você não pode evitar esta segunda try-catch. por exemplo. para fechar um fluxo

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

Por que você quer evitar o bloco adicional? Desde o último bloco contém operações "normais", que pode lançar uma exceção E você quer o bloco finally para executar completamente você tem que capturar exceções.

Se você não espera que o bloco finally para lançar uma exceção e você não sabe como lidar com a exceção de qualquer maneira (você simplesmente despejar rastreamento de pilha) deixe a bolha exceção na pilha de chamada (remover o try-catch do bloco finally).

Se você quiser reduzir a digitação você poderia implementar um bloco try-catch "global" exterior, que vai pegar todas as exceções lançadas em blocos finally:

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

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

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

Depois de muita consideração, eu acho o seguinte código melhor:

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

Isso garante código a seguir:

  1. O recurso é liberado quando o código terminou
  2. Exceções lançadas ao fechar o recurso não são consumidos sem processá-los.
  3. O código não tentar fechar o recurso duas vezes, nenhuma exceção desnecessária será criado.

Se você pode, você deve testar para evitar a condição de erro, para começar.

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

Além disso, você provavelmente só deve estar pegando exceções que você pode recuperar, se você não pode recuperar, em seguida, deixá-lo propagar para o nível superior do seu programa. Se você não puder teste para uma condição de erro que você terá que cercar o seu código com um bloco try catch como você já fez (embora eu recomendaria ainda específica captura, erros esperados).

Você poderia refatorar isso em outro método ...

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

Eu costumo fazer isso:

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

Justificativa: Se eu sou feito com o recurso e o único problema que tenho é fechá-lo, não há muito que eu possa fazer sobre isso. Não faz sentido tanto para matar o tópico inteiro se eu sou feito com o recurso de qualquer maneira.

Este é um dos casos em que, pelo menos para mim, é seguro ignorar que exceção verificada.

Para este dia, eu não tive nenhum problema usando este idioma.

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

Job. Nenhum teste nulos. Único captura, incluem aquisição e liberação de exceções. Claro que você pode usar a Cerca Executar idioma e só tem que escrever uma vez para cada tipo de recurso.

Alterar Resource de melhor resposta para Closeable

Streams implementos Closeable Assim, você pode reutilizar o método para todos os fluxos

protected void closeQuietly(Closeable resource) {
    if (resource == null) 
        return;
    try {
        resource.close();
    } catch (IOException e) {
        //log the exception
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top