Bloco finally em Java
Execute código de limpeza com blocos finally que sempre rodam, independente de uma exceção ter sido lançada ou não.
Um bloco finally é executado independentemente de como o try termina — conclusão normal, exceção capturada, exceção não capturada, ou até mesmo um return antecipado. Essa garantia é seu único propósito: é onde você coloca a limpeza que precisa acontecer, independentemente de tudo. Fechar um handle de arquivo, liberar um lock, restaurar o estado de uma thread — qualquer coisa cuja ausência deixaria o programa em uma condição pior do que quando começou.
A estrutura
try {
// risky code
} catch (SomeException e) {
// optional — zero or more catches
} finally {
// always runs after the try (and any matching catch)
}Você pode combinar finally com catch, sem nenhum catch (try { ... } finally { ... }), ou com múltiplos catches. As partes se compõem livremente.
O que "sempre executa" significa
Um bloco finally é executado quando o controle sai do try, independentemente de como:
- Fim normal do bloco
try—finallyexecuta após a última instrução. - Uma exceção lançada do
try—finallyexecuta após ocatchcorrespondente terminar (ou, se nenhum catch corresponder, logo antes de a exceção se propagar). returndentro detryoucatch—finallyexecuta antes de o retorno realmente ter efeito.breakoucontinueque sairia dotry—finallyexecuta antes do salto.
As únicas formas de ignorar o finally são: a própria JVM morre (System.exit, falta de energia, Runtime.halt), um loop infinito ou deadlock dentro do try, ou Thread.stop (que é depreciado exatamente por essa razão). Para tudo que você escreve em código de aplicação normal, finally é uma garantia rígida.
try {
return computeAnswer(); // even though there's a return here,
} finally {
cleanup(); // this runs before the method actually returns
}Para que serve o finally
A resposta honesta é: limpeza de recursos, quase sempre. Antes do Java 7 introduzir o try-with-resources, a forma canônica era:
InputStream in = null;
try {
in = new FileInputStream(path);
// read from in...
} catch (IOException e) {
// handle
} finally {
if (in != null) {
try { in.close(); } catch (IOException ignored) { /* */ }
}
}Esse try/catch aninhado em torno de close() no finally é exatamente o tipo de ruído que o try-with-resources foi criado para eliminar. Veremos isso no próximo capítulo. Mas entender para que o finally serve torna o novo construto mais compreensível.
Além de recursos, finally é útil para:
- Restaurar estado compartilhado que você mutou durante o
try— incrementar um contador de profundidade, alternar um flag, trocar umThreadLocal. - Liberar locks adquiridos manualmente (
Lock.lock()→try { ... } finally { lock.unlock(); }). - Parar temporizadores ou fechar transações que não implementam
AutoCloseable.
Para que o finally não serve
Não escreva lógica que produz resultados em finally. O bloco executa independentemente do resultado — ele não sabe se o try teve sucesso. Se você colocar commit() em finally, você fará commit mesmo em caso de falha.
E não use return em finally. Este é um dos cantos genuinamente perigosos da linguagem:
try {
return 1;
} finally {
return 2; // wins — the method returns 2 and the original return is lost
}O return (ou throw) dentro de finally sobrepõe qualquer retorno ou exceção do try. A exceção que estava prestes a se propagar é silenciosamente descartada. A maioria dos linters sinaliza isso como erro por esse motivo. A regra: finally faz limpeza; finally não produz valores.
Ordem de execução
Quando um catch e um finally estão presentes:
try { ... }
catch { ... }
finally { ... }A ordem é exatamente a que você esperaria: try executa, se lançar uma exceção e um catch corresponder, esse catch executa, e finally executa após whichever of those did. Se finally então lançar uma exceção, esse novo lançamento substitui o que estava se propagando do try ou catch — mais uma razão para manter o finally quieto.
Um exemplo prático
Instrumentamos uma pequena "transação" com try/catch/finally e a chamamos de três maneiras diferentes: sucesso normal, falha recuperável e uma irrecuperável. O finally executa nos três casos, que é o ponto central.
Na terceira chamada, doWork lança uma RuntimeException que o catch local não corresponde. O finally ainda executa e imprime "release lock" antes de a exceção continuar se propagando até main. Essa é a propriedade que você quer do código de limpeza — ela não depende de o tratamento ter tido sucesso.
O que vem a seguir
O padrão "abrir um recurso, trabalhar com ele, fechá-lo em finally" é tão comum que Java criou uma instrução dedicada para isso. Continue para Java try-with-resources.