Um bloco final sempre é executado em Java?


2382

Considerando esse código, posso ter certeza absoluta de que o finallybloco sempre é executado, não importa o que something()seja?

try {  
    something();  
    return success;  
}  
catch (Exception e) {   
    return failure;  
}  
finally {  
    System.out.println("I don't know if this will get printed out");
}

473
Caso contrário, a palavra-chave deve ser nomeada probably.
Noon Silk




27
@BinoyBabu, finalizador ! = finally; finalizador == o finalize()método
112816 jok0646

Respostas:


2699

Sim, finallyserá chamado após a execução dos blocos de código tryou catch.

Os únicos horários finallyque não serão chamados são:

  1. Se você invocar System.exit()
  2. Se você invocar Runtime.getRuntime().halt(exitStatus)
  3. Se a JVM travar primeiro
  4. Se a JVM atingir um loop infinito (ou alguma outra instrução não interrompível e não terminável) no bloco tryoucatch
  5. Se o SO finalizar forçosamente o processo da JVM; por exemplo, kill -9 <pid>no UNIX
  6. Se o sistema host morrer; por exemplo, falha de energia, erro de hardware, pânico no SO, etc.
  7. Se o finallybloco for executado por um encadeamento daemon e todos os outros encadeamentos não daemon finallyforem encerrados antes, será chamado

44
Na verdade thread.stop(), não necessariamente impede que o finallybloco seja executado.
Piotr Findeisen 30/03

181
Que tal dizermos que o finallybloco será chamado após o trybloco e antes que o controle passe para as seguintes instruções. Isso é consistente com o bloco try que envolve um loop infinito e, portanto, o bloco final nunca é realmente chamado.
Andrzej Doyle

9
há também um outro caso, quando usamos aninhados try-catch-finally blocos
ruhungry

7
Além disso, finalmente o bloco não é chamado em caso de exceção lançada pelo encadeamento daemon.
Amrish Pandey

14
@BinoyBabu - Isso é cerca de finalizador, não finalmente bloquear
avmohan

567

Código de exemplo:

public static void main(String[] args) {
    System.out.println(Test.test());
}

public static int test() {
    try {
        return 0;
    }
    finally {
        System.out.println("finally trumps return.");
    }
}

Resultado:

finally trumps return. 
0

18
FYI: No C #, o comportamento é idêntico, exceto pelo fato de não ser permitido substituir a instrução na finallycláusula-por return 2;(Erro do compilador).
Alexander Pacha

15
Aqui está um detalhe importante a ser observado: stackoverflow.com/a/20363941/2684342
WoodenKitty

18
Você pode até adicionar uma declaração de retorno no próprio bloco final, que substituirá o valor de retorno anterior. Isso também descarta magicamente exceções não tratadas. Nesse ponto, considere refatorar seu código.
Zyl

8
Isso realmente não prova que finalmente trunfos retornam. O valor de retorno é impresso a partir do código do chamador. Não parece provar muito.
precisa saber é o seguinte

20
Desculpe, mas esta é uma demonstração, não uma prova. É apenas uma prova se você pode mostrar que este exemplo sempre se comporta dessa maneira em todas as plataformas Java E que exemplos semelhantes também sempre se comportam dessa maneira.
Stephen C

391

Além disso, embora seja uma prática ruim, se houver uma declaração de retorno dentro do bloco final, ela superará qualquer outro retorno do bloco regular. Ou seja, o seguinte bloco retornaria false:

try { return true; } finally { return false; }

A mesma coisa com exceções lançadas no bloco final.


95
Esta é uma prática MUITO ruim. Consulte stackoverflow.com/questions/48088/… para obter mais informações sobre por que é ruim.
John Meagher

23
Acordado. Um retorno dentro de finalmente {} ignora qualquer exceção lançada na tentativa {}. Assustador!
Neu242 22/10/08

8
@ dominicbri7 Por que você acha que é uma prática melhor? E por que deveria ser diferente quando a função / método é nula?
corsiKa

8
Pela mesma razão, eu nunca uso goto nos meus códigos C ++. Eu acho que vários retornos dificultam a leitura e a depuração (é claro que em casos realmente simples não se aplica). Eu acho que é preferrence apenas personnal e no final você pode conseguir a mesma coisa usando qualquer método
dominicbri7

16
Costumo usar uma série de retornos quando algum tipo de caso excepcional acontece. Como se (existe um motivo para não continuar) retornar;
IHearGeoff

257

Aqui estão as palavras oficiais da Java Language Specification.

14.20.2 Execução de tentar finalmente e tentar pegar finalmente

Uma tryinstrução com um finallybloco é executada executando primeiro o trybloco. Depois, há uma escolha:

  • Se a execução do try bloco for concluída normalmente, [...]
  • Se a execução do trybloco for concluída abruptamente por causa throwde um valor V , [...]
  • Se a execução do trybloco for concluída abruptamente por qualquer outro motivo R , o finallybloco será executado. Depois, há uma escolha:
    • Se o bloco finally concluído normalmente, em seguida, a trydeclaração conclui abruptamente para a razão R .
    • Se o finallybloco for concluído abruptamente pelo motivo S , a tryinstrução será concluída abruptamente pelo motivo S ( e o motivo R será descartado ).

A especificação para returnrealmente torna isso explícito:

JLS 14.17 A declaração de retorno

ReturnStatement:
     return Expression(opt) ;

Uma returndeclaração sem Expression tentativas de transferir o controle para o invocador do método ou construtor que o contém.

Uma returndeclaração com uma Expression tentativa de transferir o controle para o invocador do método que o contém; o valor de Expressiontorna - se o valor da invocação do método.

As descrições anteriores dizem " tentativas de transferir o controle " em vez de apenas " transferem o controle " porque, se houver alguma tryinstrução no método ou construtor cujos tryblocos contenham a returninstrução, quaisquer finallycláusulas dessas tryinstruções serão executadas, em ordem, da mais interna para a mais externa , antes do controle ser transferido para o invocador do método ou construtor. A conclusão abrupta de uma finallycláusula pode interromper a transferência de controle iniciada por uma returndeclaração.


163

Além das outras respostas, é importante ressaltar que 'finalmente' tem o direito de substituir qualquer exceção / valor retornado pelo bloco try..catch. Por exemplo, o código a seguir retorna 12:

public static int getMonthsInYear() {
    try {
        return 10;
    }
    finally {
        return 12;
    }
}

Da mesma forma, o método a seguir não gera uma exceção:

public static int getMonthsInYear() {
    try {
        throw new RuntimeException();
    }
    finally {
        return 12;
    }
}

Enquanto o método a seguir o lança:

public static int getMonthsInYear() {
    try {
        return 12;          
    }
    finally {
        throw new RuntimeException();
    }
}

63
Deve-se notar que o caso do meio é precisamente a razão pela qual ter uma declaração de retorno dentro de um bloco finalmente é absolutamente horrível (poderia ocultar qualquer Throwable).
Dimitris Andreou

2
Quem não quer um suprimido OutOfMemoryError? ;)
RecursiveExceptionException

Eu testei e suprime esse erro (yipes!). Também gera um aviso quando eu o compilo (yay!). E você pode trabalhar em torno dele, definindo uma variável de retorno e, em seguida, usando return retVal depois o finallybloco, apesar de que, naturalmente, pressupõe que você suprimiu algumas outras exceções porque o código não faria sentido contrário.
Maarten Bodewes

120

Eu tentei o exemplo acima com pequenas modificações

public static void main(final String[] args) {
    System.out.println(test());
}

public static int test() {
    int i = 0;
    try {
        i = 2;
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
    }
}

O código acima gera:

finalmente supera o retorno.
2

Isso ocorre porque quando return i;executado ié um valor 2. Depois disso, o finallybloco é executado onde 12 é atribuído ie, em seguida System.out, é executado fora.

Depois de executar o finallybloco, otry bloco retorna 2, em vez de retornar 12, porque essa instrução de retorno não é executada novamente.

Se você depurar esse código no Eclipse, sentirá que, após a execução System.outdo finallybloco, a returninstrução tryblock será executada novamente. Mas esse não é o caso. Ele simplesmente retorna o valor 2.


10
Este exemplo é incrível, adiciona algo que não foi mencionado em dezenas de tópicos finalmente relacionados. Eu acho que quase nenhum desenvolvedor saberá disso.
que você

4
E se inão fosse um objeto primitivo, mas um inteiro.
Yamcha

Estou com dificuldades para entender este caso. docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.17 diz que "Uma declaração de retorno com uma Expressão tenta transferir o controle para o invocador do método ou corpo lambda que contém it .... Se a avaliação da Expressão for concluída normalmente, produzindo um valor V .. "O que eu poderia adivinhar nesta declaração é: parece que o retorno não avalia a expressão novamente depois que avalia o valor V, é por isso que mudou i não afeta o valor retornado, corrija-me.
precisa saber é o seguinte

Mas eu não encontrei nenhuma prova disso, onde é mencionado que o retorno não avalia a expressão novamente.
meexplorer

1
@meexplorer um pouco atrasado, mas é explicado no JLS 14.20.2. A execução de tentar finalmente e tentar pegar finalmente - formulou um pouco complicado, 14,17. A declaração de retorno também deve ser lida
user85421 17/17/17

117

Aqui está uma elaboração da resposta de Kevin . É importante saber que a expressão a ser retornada é avaliada antes finally, mesmo se for retornada depois.

public static void main(String[] args) {
    System.out.println(Test.test());
}

public static int printX() {
    System.out.println("X");
    return 0;
}

public static int test() {
    try {
        return printX();
    }
    finally {
        System.out.println("finally trumps return... sort of");
    }
}

Resultado:

X
finally trumps return... sort of
0

8
Importante saber.
Aminadav Glickshtein

É bom saber e também faz sentido. Parece que realmente retornar o valor do retorno é o que vem depois finally. O cálculo do valor de retorno ( printX()aqui) ainda vem antes dele.
26619 Albert Albert

um bom exemplo com todos os 3 pontos "retornando"!
radistao 03/01

Não. O código acima deve substituir System.out.println("finally trumps return... sort of");porSystem.out.print("finally trumps return in try"); return 42;
Pacerier

54

Essa é toda a idéia de um bloco finalmente. Ele permite que você faça limpezas que, de outra forma, poderiam ser ignoradas porque você retorna, entre outras coisas, é claro.

Finalmente, é chamado independentemente do que acontece no bloco try (a menos que você chame System.exit(int)ou a Java Virtual Machine comece por algum outro motivo).


Essa é uma resposta extremamente fraca. stackoverflow.com/a/65049/715269
Gangnus

42

Uma maneira lógica de pensar sobre isso é:

  1. O código colocado em um bloco final deve ser executado o que ocorrer dentro do bloco try
  2. Portanto, se o código no bloco try tentar retornar um valor ou lançar uma exceção, o item será colocado 'na prateleira' até que o bloco finalmente possa ser executado
  3. Como o código no bloco final tem (por definição) uma alta prioridade, ele pode retornar ou lançar o que quiser. Nesse caso, qualquer coisa deixada na prateleira é descartada.
  4. A única exceção é se a VM for encerrada completamente durante o bloco try, por exemplo, por 'System.exit'

10
É apenas "uma maneira lógica de pensar sobre isso" ou é realmente como o bloco finalmente deve funcionar de acordo com as especificações? Um link para um recurso da Sun seria muito interessante aqui.
Matias 26/05

21

finalmente é sempre executado, a menos que haja um encerramento anormal do programa (como chamar System.exit (0) ..). então, seu sysout será impresso



18

O bloco final é sempre executado, a menos que haja um encerramento anormal do programa, resultante de uma falha na JVM ou de uma chamada para System.exit(0).

Além disso, qualquer valor retornado do bloco final substituirá o valor retornado antes da execução do bloco final, portanto, verifique todos os pontos de saída ao usar try finalmente.


18

Não, nem sempre um caso de exceção é // System.exit (0); antes do bloco finally impede que finalmente seja executado.

  class A {
    public static void main(String args[]){
        DataInputStream cin = new DataInputStream(System.in);
        try{
            int i=Integer.parseInt(cin.readLine());
        }catch(ArithmeticException e){
        }catch(Exception e){
           System.exit(0);//Program terminates before executing finally block
        }finally{
            System.out.println("Won't be executed");
            System.out.println("No error");
        }
    }
}

E essa é uma das razões pelas quais você realmente nunca deve chamar System.exit () ...
Franz D.

13

Finalmente, sempre é executado. Esse é o ponto, apenas porque ele aparece no código após o retorno não significa que é assim que é implementado. O Java Runtime tem a responsabilidade de executar esse código ao sair do trybloco.

Por exemplo, se você tiver o seguinte:

int foo() { 
    try {
        return 42;
    }
    finally {
        System.out.println("done");
    }
}

O tempo de execução irá gerar algo como isto:

int foo() {
    int ret = 42;
    System.out.println("done");
    return 42;
}

Se uma exceção não capturada for lançada, o finallybloco será executado e a exceção continuará se propagando.


11

Isso ocorre porque você atribuiu o valor de i como 12, mas não retornou o valor de i à função. O código correto é o seguinte:

public static int test() {
    int i = 0;
    try {
        return i;
    } finally {
        i = 12;
        System.out.println("finally trumps return.");
        return i;
    }
}

10

Porque um bloco final sempre será chamado, a menos que você ligue System.exit()(ou o encadeamento trava).


10

Concisa, na documentação oficial do Java (clique aqui ), está escrito que -

Se a JVM sair enquanto o código try ou catch estiver sendo executado, o bloco finalmente poderá não ser executado. Da mesma forma, se o encadeamento que executa o código try ou catch for interrompido ou eliminado, o bloco finalmente pode não ser executado, mesmo que o aplicativo como um todo continue.


10

A resposta é simples SIM .

ENTRADA:

try{
    int divideByZeroException = 5 / 0;
} catch (Exception e){
    System.out.println("catch");
    return;    // also tried with break; in switch-case, got same output
} finally {
    System.out.println("finally");
}

RESULTADO:

catch
finally

1
A resposta é simples NÃO.
Christophe Roussy

1
@ChristopheRoussy Como? Você pode explicar por favor?
Conheça

1
Leia a resposta aceita, a pergunta original é sobre 'Será sempre executada' e nem sempre. No seu caso, ele responderá, mas isso não responde à pergunta original e pode até ser iniciantes enganosos.
Christophe Roussy

então, nesse caso, ele não será executado?
Conheça

Em todos os casos mencionados em outras respostas, consulte a resposta aceita com mais de 1000 upvotes.
Christophe Roussy

9

Sim, será chamado. Esse é o objetivo de ter finalmente uma palavra-chave. Se pular do bloco try / catch pudesse pular o bloco final, seria o mesmo que colocar o System.out.println fora do try / catch.


9

Sim, finalmente o bloco é sempre executado. A maioria dos desenvolvedores usa esse bloco para fechar a conexão com o banco de dados, o objeto de conjunto de resultados, o objeto de instrução e também usa no hibernate java para reverter a transação.


9

NEM SEMPRE

A especificação Java Idioma descreve como try- catch- finallye try- catchblocos de trabalhar em 14.20.2
Em nenhum lugar ele especifica que o finallybloco é sempre executado. Mas, para todos os casos em que o try- catch- finallye try- finallyblocos de concluí-lo não especifica que antes da conclusão finallydeve ser executado.

try {
  CODE inside the try block
}
finally {
  FIN code inside finally block
}
NEXT code executed after the try-finally block (may be in a different method).

O JLS não garante que o FIN seja executado após o CODE . O JLS garante que, se CODE e NEXT forem executados, FIN será sempre executado após CODE e antes de NEXT .

Por que o JLS não garante que o finallybloco seja sempre executado após o trybloco? Porque é impossível. É improvável, mas possível, que a JVM seja abortada (interrupção, falha, desligamento) logo após concluir o trybloco, mas antes da execução dofinally bloco. Não há nada que o JLS possa fazer para evitar isso.

Assim, qualquer software que, para o seu bom comportamento, dependa de finallyblocos sempre executados após a tryconclusão dos mesmos, são corrigidos.

returninstruções no trybloco são irrelevantes para esse problema. Se a execução atingir o código após o try- catch- finally, é garantido que o finallybloco tenha sido executado antes, com ou sem returninstruções dentro do trybloco.


8

Sim vai. Não importa o que aconteça em seu bloco try ou catch, a menos que System.exit () chamado ou a JVM tenha travado. se houver alguma declaração de retorno no (s) bloco (s), finalmente será executada antes dessa declaração de retorno.


8

Sim vai. O único caso em que a JVM sairá ou trava


8

Adicionar a resposta do @ vibhash como nenhuma outra resposta explica o que acontece no caso de um objeto mutável como o abaixo.

public static void main(String[] args) {
    System.out.println(test().toString());
}

public static StringBuffer test() {
    StringBuffer s = new StringBuffer();
    try {
        s.append("sb");
        return s;
    } finally {
        s.append("updated ");
    }
}

Saída

sbupdated 

A partir do Java 1.8.162, essa não é a saída.
Sam

8

Eu tentei isso, é único thread.

public static void main(String args[]) throws Exception {
    Object obj = new Object();
    try {
        synchronized (obj) {
            obj.wait();
            System.out.println("after wait()");
        }
    } catch (Exception ignored) {
    } finally {
        System.out.println("finally");
    }
}

O estado main Threadestará em waitestado para sempre, portanto finallynunca será chamado ,

portanto, a saída do console não print String: depois wait()oufinally

Concordado com @Stephen C, o exemplo acima é um dos terceiro casos mencionados aqui :

Adicionando mais algumas possibilidades de loop infinito no seguinte código:

// import java.util.concurrent.Semaphore;

public static void main(String[] args) {
    try {
        // Thread.sleep(Long.MAX_VALUE);
        // Thread.currentThread().join();
        // new Semaphore(0).acquire();
        // while (true){}
        System.out.println("after sleep join semaphore exit infinite while loop");
    } catch (Exception ignored) {
    } finally {
        System.out.println("finally");
    }
}

Caso 2: se a JVM travar primeiro

import sun.misc.Unsafe;
import java.lang.reflect.Field;

public static void main(String args[]) {
    try {
        unsafeMethod();
        //Runtime.getRuntime().halt(123);
        System.out.println("After Jvm Crash!");
    } catch (Exception e) {
    } finally {
        System.out.println("finally");
    }
}

private static void unsafeMethod() throws NoSuchFieldException, IllegalAccessException {
    Field f = Unsafe.class.getDeclaredField("theUnsafe");
    f.setAccessible(true);
    Unsafe unsafe = (Unsafe) f.get(null);
    unsafe.putAddress(0, 0);
}

Ref: Como você trava uma JVM?

Caso 6: Se o finallybloco será executado pelo daemonThread e todas as outras Threadssaídas não-daemon anteriores finallyforem chamadas.

public static void main(String args[]) {
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                printThreads("Daemon Thread printing");
                // just to ensure this thread will live longer than main thread
                Thread.sleep(10000);
            } catch (Exception e) {
            } finally {
                System.out.println("finally");
            }
        }
    };
    Thread daemonThread = new Thread(runnable);
    daemonThread.setDaemon(Boolean.TRUE);
    daemonThread.setName("My Daemon Thread");
    daemonThread.start();
    printThreads("main Thread Printing");
}

private static synchronized void printThreads(String str) {
    System.out.println(str);
    int threadCount = 0;
    Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
    for (Thread t : threadSet) {
        if (t.getThreadGroup() == Thread.currentThread().getThreadGroup()) {
            System.out.println("Thread :" + t + ":" + "state:" + t.getState());
            ++threadCount;
        }
    }
    System.out.println("Thread count started by Main thread:" + threadCount);
    System.out.println("-------------------------------------------------");
}

saída: Isso não imprime "Finalmente", o que implica que "Finalmente o bloco" no "daemon thread" não foi executado

main Thread Printing  
Thread :Thread[My Daemon Thread,5,main]:state:BLOCKED  
Thread :Thread[main,5,main]:state:RUNNABLE  
Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE   
Thread count started by Main thread:3  
-------------------------------------------------  
Daemon Thread printing  
Thread :Thread[My Daemon Thread,5,main]:state:RUNNABLE  
Thread :Thread[Monitor Ctrl-Break,5,main]:state:RUNNABLE  
Thread count started by Main thread:2  
-------------------------------------------------  

Process finished with exit code 0

4
Veja a resposta aceita. Este é apenas um caso extremo de "loop infinito".
Stephen C

8

Considere o seguinte programa:

public class SomeTest {

    private static StringBuilder sb = new StringBuilder();

    public static void main(String args[]) {

        System.out.println(someString());
        System.out.println("---AGAIN---");
        System.out.println(someString());
        System.out.println("---PRINT THE RESULT---");
        System.out.println(sb.toString());
    }

    private static String someString() {

        try {
            sb.append("-abc-");
            return sb.toString();

        } finally {
            sb.append("xyz");
        }
    }
}

A partir do Java 1.8.162, o bloco de código acima fornece a seguinte saída:

-abc-
---AGAIN---
-abc-xyz-abc-
---PRINT THE RESULT---
-abc-xyz-abc-xyz

isso significa que usar finallypara liberar objetos é uma boa prática, como o código a seguir:

private static String someString() {

    StringBuilder sb = new StringBuilder();

    try {
        sb.append("abc");
        return sb.toString();

    } finally {
        sb = null; // Just an example, but you can close streams or DB connections this way.
    }
}

Não deveria estar sb.setLength(0)finalmente?
user7294900

sb.setLength (0) apenas esvaziará os dados no StringBuffer. Portanto, sb = null desassociará o objeto da referência.
Sam

"Xyz" não deve ser impresso duas vezes na saída? Como a função foi chamada duas vezes, por que "finalmente" foi apenas uma vez?
fresko

2
Esta não é uma boa prática. Esse bloco finalmente sb = null;adiciona apenas um código desnecessário. Entendo que você quer dizer que um finallybloco é um bom lugar para liberar recursos como uma conexão com o banco de dados ou algo assim, mas lembre-se de que seu exemplo pode confundir os recém-chegados.
Roc Boronat

1
@ Samim Obrigado, eu adicionei as linhas System.out.println("---AGAIN2---"); System.out.println(sb);e é mais claro agora. Como foi, a saída foi contrária à sua tese: p Adicionei também a sua resposta, mas a edição deve ser aceita por um moderador ou alguém assim.
Caso contrário

7

Isso é verdade em qualquer idioma ... finalmente, sempre será executado antes de uma declaração de retorno, não importa onde esse retorno esteja no corpo do método. Se não fosse esse o caso, o bloco finalmente não teria muito significado.


7

Além do ponto sobre retorno na substituição final de um retorno no bloco try, o mesmo se aplica a uma exceção. Um bloco finalmente que lança uma exceção substitui um retorno ou exceção lançada de dentro do bloco try.


7

finally será executado e isso é certo.

finally não será executado nos casos abaixo:

caso 1 :

Quando você está executando System.exit().

caso 2:

Quando sua JVM / Thread falha.

caso 3:

Quando sua execução é interrompida manualmente.


6
  1. Finalmente, o bloco sempre é executado. A menos que e até a declaração System.exit () exista lá (primeira declaração no bloco finalmente).
  2. Se system.exit () for a primeira instrução, finalmente o bloco não será executado e o controle sairá do bloco finalmente. Sempre que a instrução System.exit () entra finalmente no bloco até que a instrução finalmente seja executada e quando System.exit () aparece, a força de controle sai totalmente do bloco finalmente.

1
Isso já foi respondido várias vezes. Portanto, quais novas informações sua resposta adiciona?
Tom
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.