Je suis conscient des maux de tête qui impliquent le retour dans les blocs try/catch/finally - cas où le retour dans le finalement est toujours le retour de la méthode, même si un retour dans un bloc try ou catch doit être celui exécuté.
Cependant, la même chose s'applique-t-elle à System.exit ()? Par exemple, si j'ai un bloc try:
try {
//Code
System.exit(0)
}
catch (Exception ex) {
//Log the exception
}
finally {
System.exit(1)
}
S'il n'y a pas d'exceptions, quel System.exit () sera appelé? Si la sortie était une instruction de retour, la ligne System.exit (1) serait toujours (?) Appelée. Cependant, je ne suis pas sûr que la sortie se comporte différemment du retour.
Le code est dans un cas extrême qui est très difficile, voire impossible, à reproduire, donc je ne peux pas écrire un test unitaire. Je vais essayer de lancer une expérience plus tard dans la journée, si j'obtiens quelques minutes gratuites, mais je suis curieux de toute façon, et peut-être que quelqu'un sur SO connaît la réponse et peut la fournir avant ou au cas où je ne pourrais pas exécuter une expérience.
Non. System.exit(0)
ne revient pas et le bloc finally n'est pas exécuté.
System.exit(int)
peut lancer un SecurityException
. Si cela se produit, le bloc finalement sera sera exécuté. Et puisque le même principal appelle la même méthode à partir de la même base de code, un autre SecurityException
est susceptible d'être lancé à partir du deuxième appel.
Voici un exemple du deuxième cas:
import Java.security.Permission;
public class Main
{
public static void main(String... argv)
throws Exception
{
System.setSecurityManager(new SecurityManager() {
@Override
public void checkPermission(Permission perm)
{
/* Allow everything else. */
}
@Override
public void checkExit(int status)
{
/* Don't allow exit with any status code. */
throw new SecurityException();
}
});
System.err.println("I'm dying!");
try {
System.exit(0);
} finally {
System.err.println("I'm not dead yet!");
System.exit(1);
}
}
}
Des tests simples incluant catch
révèlent également que si system.exit(0)
ne lève pas d'exception de sécurité, ce sera la dernière instruction exécutée (catch
et finally
ne sont pas exécutés du tout).
Si system.exit(0)
lève une exception de sécurité, les instructions catch
et finally
sont exécutées. Si catch
et finally
contiennent des instructions system.exit()
, seules les instructions précédant ces instructions system.exit()
sont exécutées.
Dans les deux cas décrits ci-dessus, si le code try
appartient à une méthode appelée par une autre méthode, la méthode appelée ne retourne pas.
Plus de détails ici (blog personnel).
D'autres réponses ont expliqué comment les blocs catch
et finally
ne s'exécutent pas si System.exit
quitte la JVM sans lancer un SecurityException
, mais ils ne montrent pas ce qui se passe dans un bloc "try-with-resources" aux ressources: sont-elles fermées?
Selon le JLS, section 14.20.3.2 :
La traduction a pour effet de placer la spécification de ressource "à l'intérieur" de l'instruction try. Cela permet à une clause catch d'une instruction try-with-resources étendue d'intercepter une exception en raison de l'initialisation ou de la fermeture automatique de toute ressource.
De plus, toutes les ressources auront été fermées (ou tentées de l'être) au moment où le bloc finally est exécuté, conformément à l'intention du mot clé finally.
Autrement dit, les ressources seront close
d avant l'exécution d'un bloc catch
ou finally
. Que faire s'ils sont close
d d'une manière ou d'une autre même si catch
et finally
ne s'exécutent pas?
Voici du code pour démontrer que les ressources d'une instruction "try-with-resources" ne sont pas fermées non plus.
J'utilise une simple sous-classe de BufferedReader
qui imprime une instruction avant d'appeler super.close
.
class TestBufferedReader extends BufferedReader {
public TestBufferedReader(Reader r) {
super(r);
}
@Override
public void close() throws IOException {
System.out.println("close!");
super.close();
}
}
Ensuite, j'ai mis en place le cas de test d'appeler System.exit
dans l'instruction try-with-resources.
public static void main(String[] args)
{
try (BufferedReader reader = new TestBufferedReader(new InputStreamReader(System.in)))
{
System.out.println("In try");
System.exit(0);
}
catch (Exception e)
{
System.out.println("Exception of type " + e.getClass().getName() + " caught: " + e.getMessage());
}
finally
{
System.out.println("finally!");
}
}
Sortie:
En essai
Par conséquent, non seulement les blocs catch
et finally
ne s'exécutent pas, mais une instruction "essayer avec des ressources" n'aura pas la chance de close
ses ressources si System.exit
réussit.
enfin le bloc sera exécuté quoi qu'il arrive .... même si le bloc try lance n'importe quel objet jetable (exception ou erreur) .....
le seul cas où finalement le bloc ne s'exécute pas ... c'est lorsque nous appelons la méthode System.exit ().
try{
System.out.println("I am in try block");
System.exit(1);
} catch(Exception ex){
ex.printStackTrace();
} finally {
System.out.println("I am in finally block!!!");
}
Il n'exécutera pas finalement le bloc. Le programme sera terminé après l'instruction System.exit ().
Si vous considérez ce comportement problématique et que vous avez besoin d'un contrôle précis sur vos appels System.exit
, La seule chose que vous pouvez faire est d'encapsuler la fonctionnalité System.exit dans votre propre logique. Si nous le faisons, nous pouvons enfin exécuter les blocs et fermer les ressources dans le cadre de notre flux de sortie.
Ce que j'envisage de faire, c'est d'encapsuler l'appel et la fonctionnalité System.exit
Dans ma propre méthode statique. Dans mon implémentation de exit
, je lancerais une sous-classe personnalisée de Throwable
ou Error
, et implémenterais un gestionnaire d'exceptions Uncaught personnalisé avec Thread.setDefaultUncaughtExceptionHandler
Pour gérer cette exception. Ainsi mon code devient:
//in initialization logic:
Thread.setDefaultUncaughtExceptionHandler((thread, exception) -> {
if(exception instanceof SystemExitEvent){
System.exit(((SystemExitEvent)exception).exitCode);
}
})
// in "main flow" or "close button" or whatever
public void mainFlow(){
try {
businessLogic();
Utilities.exit(0);
}
finally {
cleanUpFileSystemOrDatabaseConnectionOrWhatever();
}
}
//...
class Utilities {
// I'm not a fan of documentaiton,
// but this method could use it.
public void exit(int exitCode){
throw new SystemExitEvent(exitCode);
}
}
class SystemExitEvent extends Throwable {
private final int exitCode;
public SystemExitEvent(int exitCode){
super("system is shutting down")
this.exitCode = exitCode;
}
}
Cette stratégie a l'avantage supplémentaire de rendre cette logique testable: pour tester que la méthode contenant notre "flux principal" demande réellement au système de quitter, tout ce que nous avons à faire est d'attraper un objet jetable et d'affirmer qu'il s'agit du type d'écriture. Par exemple, un test pour notre wrapper de logique métier peut ressembler à:
//kotlin, a really Nice language particularly for testing on the JVM!
@Test fun `when calling business logic should business the business`(){
//setup
val underTest = makeComponentUnderTest(configureToReturnExitCode = 42);
//act
val thrown: SystemExitEvent = try {
underTest.mainFlow();
fail("System Exit event not thrown!")
}
catch(event: SystemExitEvent){
event;
}
//assert
assertThat(thrown.exitCode).isEqualTo(42)
L'inconvénient majeur de cette stratégie est qu'elle permet de tirer des fonctionnalités d'un flux d'exception, ce qui a souvent des conséquences inattendues. Le plus évident, dans ce cas, est que partout où vous avez écrit try { ... } catch(Throwable ex){ /*doesnt rethrow*/ }
devra être mis à jour. Dans le cas des bibliothèques qui ont des contextes d'exécution personnalisés, elles devront être mises à niveau pour comprendre également cette exception.
Dans l'ensemble, cela me semble être une bonne stratégie. Quelqu'un d'autre ici le pense-t-il?
Dans l'exemple ci-dessous, si System.exit(0)
est avant la ligne d'exception, le programme se terminera normalement, donc FINALLY ne s'exécutera pas.
Si la System.exix(0)
est la dernière ligne du bloc try, nous avons ici 2 scénarios
.
package com.exception;
public class UserDefind extends Exception {
private static int accno[] = {1001,1002,1003,1004,1005};
private static String name[] = {"raju","ramu","gopi","baby","bunny"};
private static double bal[] = {9000.00,5675.27,3000.00,1999.00,1600.00};
UserDefind(){}
UserDefind(String str){
super(str);
}
public static void main(String[] args) {
try {
//System.exit(0); -------------LINE 1---------------------------------
System.out.println("accno"+"\t"+"name"+"\t"+"balance");
for (int i = 0; i < 5; i++) {
System.out.println(accno[i]+"\t"+name[i]+"\t"+bal[i]);
//rise exception if balance < 2000
if (bal[i] < 200) {
UserDefind ue = new UserDefind("Balance amount Less");
throw ue;
}//end if
}//end for
//System.exit(0);-------------LINE 2---------------------------------
}//end try
catch (UserDefind ue)
{
System.out.println(ue);
}
finally{
System.out.println("Finnaly");
System.out.println("Finnaly");
System.out.println("Finnaly");
}
}//end of main
}//end of class