Question Est-ce que finalement toujours exécuter en Java?


Considérant ce code, puis-je être absolument certain que le finally bloc exécute toujours, peu importe ce que something() est?

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

1901
2017-09-15 17:43


origine


Réponses:


Oui, finally sera appelé après l'exécution des blocs de code try ou catch.

Les seuls moments finally ne sera pas appelé sont:

  1. Si vous invoquez System.exit();
  2. Si la JVM se bloque en premier;
  3. Si la JVM atteint une boucle infinie (ou une autre instruction non interrompue, sans fin) dans le try ou catch bloc;
  4. Si le système d'exploitation termine de manière forcée le processus JVM; par exemple. "kill -9" sur UNIX.
  5. Si le système hôte meurt; par exemple. panne de courant, erreur matérielle, panique d'OS, etc.
  6. Si finalement le bloc va être exécuté par le thread démon et tous les autres threads non démon sortent avant d'être finalement appelé.

2114
2017-09-15 17:45



Exemple de code:

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

Sortie:

finally trumps return. 
0

444
2017-09-15 17:59



De plus, bien que ce soit une mauvaise pratique, s'il y a une déclaration de retour dans le bloc finally, cela annulera tout autre retour du bloc normal. Autrement dit, le bloc suivant renverrait false:

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

La même chose avec jeter des exceptions du bloc finally.


323
2017-09-15 18:19



Voici les mots officiels de la spécification du langage Java.

14.20.2. Exécution de try-finally et try-catch-finally

UNE try déclaration avec un finally bloc est exécuté en exécutant d'abord le try bloc. Ensuite, il y a un choix:

  • Si l'exécution du try bloc se termine normalement, [...]
  • Si l'exécution du try bloc se termine brusquement à cause d'un throw d'une valeur V, [...]
  • Si l'exécution du try bloc se termine brusquement pour toute autre raison R, puis le finally le bloc est exécuté. Ensuite, il y a un choix:
    • Si le bloc final se termine normalement, alors le try déclaration se termine brusquement pour la raison R.
    • Si la finally bloc se termine brusquement pour raison S, puis le try déclaration se termine brusquement pour la raison S (et la raison R est jeté).

La spécification pour return rend cela explicite:

JLS 14.17 La déclaration de retour

ReturnStatement:
     return Expression(opt) ;

UNE return déclaration sans Expression  tentatives transférer le contrôle à l'invocateur de la méthode ou du constructeur qui le contient.

UNE return déclaration avec un Expression  tentatives transférer le contrôle à l'invocateur de la méthode qui le contient; la valeur de Expression devient la valeur de l'invocation de la méthode.

Les descriptions précédentes disent "tentatives transférer le contrôle"plutôt que juste"transfert de contrôle"parce que s'il y en a try déclarations au sein de la méthode ou du constructeur dont try les blocs contiennent le return déclaration, puis tout finally clauses de ceux try les instructions seront exécutées, dans l'ordre, de l'intérieur vers l'extérieur, avant que le contrôle ne soit transféré à l'invocateur de la méthode ou du constructeur. L'achèvement abrupt d'un finally clause peut perturber le transfert de contrôle initié par un return déclaration.


229
2018-05-25 06:50



En plus des autres réponses, il est important de souligner que 'finally' a le droit de remplacer toute exception / valeur renvoyée par le bloc try..catch. Par exemple, le code suivant renvoie 12:

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

De même, la méthode suivante ne lance pas d'exception:

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

Alors que la méthode suivante le lance:

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

134
2018-05-13 07:11



J'ai essayé l'exemple ci-dessus avec une légère modification ...

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

Le code ci-dessus produit:

finalement l'emporte sur le retour.
  2

C'est parce que quand return i; est exécuté i a une valeur 2. Après cela, le finally bloc est exécuté où 12 est affecté à i et alors System.out out est exécuté.

Après l'exécution du finally bloquer le try block renvoie 2, plutôt que de renvoyer 12, car cette instruction return n'est pas exécutée à nouveau.

Si vous déboguez ce code dans Eclipse, vous aurez l'impression qu'après l'exécution System.out de finally bloquer le return déclaration de try le bloc est à nouveau exécuté. Mais ce n'est pas le cas. Il renvoie simplement la valeur 2.


88
2017-11-17 16:25



Voici une élaboration de La réponse de Kevin. Il est important de savoir que l'expression à renvoyer est évaluée avant finally, même s'il est retourné après.

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

Sortie:

X
finally trumps return... sort of
0

80
2017-12-03 23:36