Pergunta

Eu apenas tive uma experiência muito dolorosa solução de problemas na solução de algum código que ficou assim:

try {
   doSomeStuff()
   doMore()
} finally {
   doSomeOtherStuff()
}

O problema foi difíceis de solucionar porque doSomeStuff () lançou uma exceção, que por sua vez causou doSomeOtherStuff () para também lançar uma exceção. A segunda exceção (acionada pelo bloco finally) foi jogado até meu código, mas não tem uma alça sobre a primeira exceção (lançada a partir de doSomeStuff ()), que era a verdadeira causa-raiz do problema.

Se o código tinha dito isso em vez disso, o problema teria sido facilmente perceptível:

try {
    doSomeStuff()
    doMore()
} catch (Exception e) {
    log.error(e);
} finally {
   doSomeOtherStuff()
}

Então, minha pergunta é esta:

é um bloco finally usado sem qualquer bloco catch um anti-padrão java bem conhecido? (Ele certamente parece ser um não-prontamente aparente subclasse da obviamente bem conhecido anti-padrão "Não exceções não devora!")

Foi útil?

Solução

Em geral, não, isso não é um anti-padrão. O ponto de blocos finally é certificar-se o material fica limpo se uma exceção é lançada ou não. O ponto inteiro de tratamento de exceção é que, se você não pode lidar com isso, você deixá-lo borbulhar para alguém que possa, através do tratamento de exceção sinalização out-of-band relativamente limpa proporciona. Se você precisa ter certeza de material fica limpo se uma exceção é lançada, mas não pode tratar adequadamente a exceção no escopo atual, então esta é exatamente a coisa certa a fazer. Você só pode querer ser um pouco mais cuidadosa sobre certificando-se o seu último bloco não lança.

Outras dicas

Eu acho que o "anti-padrão" real aqui é fazer algo em um bloco finally que pode jogar, não não ter uma captura.

Nem um pouco.

errada do que é o código dentro do fim.

Lembre-se que, finalmente, sempre terá executado, e é apenas arriscada (como você tem testemunhado apenas) para colocar algo que pode lançar uma exceção lá.

Não há absolutamente nada de errado uma tentativa com um finalmente e sem captura. Considere o seguinte:

InputStream in = null;
try {
    in = new FileInputStream("file.txt");
    // Do something that causes an IOException to be thrown
} finally {
    if (in != null) {
         try {
             in.close();
         } catch (IOException e) {
             // Nothing we can do.
         }
    }
}

Se uma exceção é lançada e este código não sabe como lidar com ele, então a exceção deve borbulhar na pilha de chamada para o chamador. Neste caso, nós ainda quer limpar o fluxo então eu acho que faz todo o sentido ter um bloco try sem um prendedor.

Eu acho que é longe de ser um anti-padrão e é algo que faço com muita frequência quando É fundamental fazer recursos desalocar obtidas durante a execução do método.

Uma coisa que eu faço quando se lida com identificadores de arquivo (por escrito) está liberando o fluxo antes de fechá-lo usando o método IOUtils.closeQuietly, que não lançar exceções:


OutputStream os = null;
OutputStreamWriter wos = null;
try { 
   os = new FileOutputStream(...);
   wos = new OutputStreamWriter(os);
   // Lots of code

   wos.flush();
   os.flush();
finally {
   IOUtils.closeQuietly(wos);
   IOUtils.closeQuietly(os);
}

Eu gosto de fazer isso dessa forma, pelas seguintes razões:

  • Não é completamente seguro ignorar uma exceção ao fechar um arquivo - se há bytes que não foram escritos para o arquivo, no entanto, em seguida, o arquivo pode não ser no estado o chamador seria de esperar;
  • Assim, se uma exceção é levantada durante o método flush (), será propagada para o chamador, mas eu ainda irá certificar-se todos os arquivos estão fechados. O método IOUtils.closeQuietly (...) é menos detalhado em seguida, o correspondente try ... catch ... me ignorar bloquear;
  • Se estiver usando saída de vários fluxos a ordem para o método flush () é importante. Os fluxos que foram criadas pela passagem de outros fluxos como construtores deve ser lavada em primeiro lugar. A mesma coisa é válida para o método close (), mas o flush () é mais claro na minha opinião.

Eu diria que um bloco try sem um bloco catch é um anti-padrão. Dizer "não tem uma finalmente sem um catch" é um subconjunto de "não ter uma tentativa sem um catch".

Eu uso try / finally da seguinte forma:

try{
   Connection connection = ConnectionManager.openConnection();
   try{
       //work with the connection;
   }finally{
       if(connection != null){
          connection.close();           
       }
   }
}catch(ConnectionException connectionException){
   //handle connection exception;
}

Eu prefiro este ao try / catch / finally (+ tentativa nested / catch na finalmente). Eu acho que é mais conciso e eu não duplicar o catch (Exception).

try {
    doSomeStuff()
    doMore()
} catch (Exception e) {
    log.error(e);
} finally {
   doSomeOtherStuff()
}

Não faça isso ou ... você apenas se escondeu mais bugs (bem, não exatamente os escondeu ... mas tornou mais difícil lidar com eles. Quando você pegar Exception você também está travando qualquer tipo de RuntimeException (como NullPointer e ArrayIndexOutOfBounds).

Em geral, capturar as exceções que você tem a (exceções verificadas) captura e lidar com os outros testar tempo. RuntimeExceptions são projetados para ser usado por erros programador -. E erros programador são coisas que não deveria acontecer em um programa devidamente depurado

Na minha opinião, é mais o caso que finally com um catch indicar algum tipo de problema. O idioma de recursos é muito simples:

acquire
try {
    use
} finally {
    release
}

Em Java você pode ter uma exceção a partir de praticamente qualquer lugar. Muitas vezes, a adquirir lança uma exceção verificada, a maneira sensata de lidar com isso é colocar um prendedor em torno da forma como muito. Não tente alguma verificação nulo hediondo.

Se você estava indo para ser realmente anal você deve observar que existem prioridades implícitas entre exceções. Por exemplo ThreadDeath deve espancar tudo, se se trata de aquisição / utilização / release. Lidar com essas prioridades corretamente é feio.

Portanto, resumo o seu recurso manipulação de acabar com o idioma Executar Around.

try / finally é uma forma de recursos adequadamente livres. O código no bloco finally NUNCA deve jogar, uma vez que só deve agir sobre recursos ou estado que foi adquirido antes da entrada no bloco try.

Como um aparte, eu acho que Log4J é quase um anti-padrão.

Se você quiser inspecionar Um programa em execução usar uma ferramenta de controlo adequado (ou seja, um depurador, IDE, ou em um extremo sentido um código byte tecelão mas não coloque DEMONSTRAÇÕES LOGGING linhas em cada poucos!).

Nos dois exemplos que você apresenta o primeiro parece correto. O segundo inclui o código logger e introduz um bug. No segundo exemplo, você suprimir uma exceção se um é jogado pelas duas primeiras instruções (ou seja, você pegá-lo e registrá-lo, mas não relançar. Isso é algo que eu acho muito comum no uso log4j e é um verdadeiro problema de design do aplicativo. Basicamente com a alteração que você fazer o programa falhar de uma maneira que seria muito difícil para o sistema para lidar desde que você basicamente marchar para a frente como se você nunca teve uma exceção (sorta como VB básica on error resume Next construção).

try-finally pode ajudá-lo a reduzir o código copiar e colar no caso de um método tem várias instruções return. Considere o seguinte exemplo (Java Android):

boolean doSomethingIfTableNotEmpty(SQLiteDatabase db) {
    Cursor cursor = db.rawQuery("SELECT * FROM table", null);
    if (cursor != null) { 
        try {
            if (cursor.getCount() == 0) { 
                return false;
            }
        } finally {
            // this will get executed even if return was executed above
            cursor.close();
        }
    }
    // database had rows, so do something...
    return true;
}

Se não houvesse cláusula finally, você pode ter que escrever cursor.close() duas vezes:., Pouco antes return false e também após a cláusula if circundante

Eu acho que tentam sem capturas é anti-padrão. Usando try / catch para tratar excepcionais condições (arquivo erros Io, tempo limite de socket, etc) não é um anti-padrão.

Se você estiver usando try / finally para limpeza, considere um bloco usando em seu lugar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top