web-dev-qa-db-fra.com

Quelle est la différence entre lancer e et lancer une nouvelle exception (e)?

Considérer:

try  {
    // Some code here
} catch (IOException e) {
    throw e;
} catch (Exception e) {
    throw e;
}

Quelle est la différence entre throw e Et throw new Exception(e)?

try  {
   // Some code here
} catch (IOException e) {
   throw new IOException(e);
} catch (Exception e) {
   throw new Exception(e);
}
38
Vinaya Nayak

Si vous n'avez pas besoin d'ajuster le type d'exception, vous relancez (jetez plus loin) la même instance sans aucune modification:

catch (IOException e) {
    throw e;
}

Si vous avez besoin d'ajuster le type d'exception, vous envelopper e (comme cause) dans une nouvelle exception du type requis.

catch (IOException e) {
    throw new IllegalArgumentException(e);
}

Je considère tous les autres scénarios comme une odeur de code. Votre deuxième extrait en est un bon bon exemple.


Voici les réponses aux questions qui pourraient apparaître.

Pourquoi voudrais-je lever une exception?

Vous pouvez laisser tomber. Mais si cela se produit, vous ne pourrez rien faire à ce niveau.

Lorsque nous interceptons une exception dans une méthode, nous sommes toujours dans cette méthode et avons accès à sa portée (par exemple, les variables locales et leur état). Avant de lever l'exception, nous pouvons faire tout ce que nous devons faire (par exemple, enregistrer un message, l'envoyer quelque part, faire un instantané de l'état actuel).

Pourquoi voudrais-je ajuster une exception?

En règle générale,

Les couches supérieures doivent intercepter les exceptions de niveau inférieur et, à leur place, lever des exceptions qui peuvent être expliquées en termes d'abstraction de niveau supérieur.

Efficace Java - 2nd Edition - Item 61: Lance des exceptions appropriées à l'abstraction

En d'autres termes, à un moment donné, un IOException obscur devrait être transformé en MySpecificBusinessRuleException perspicace.

Je l'ai appelé "ajustement du type d'exception" , les gars intelligents l'appellent traduction d'exception ( chaînage d'exceptions , notamment).


Pour être clair, ayons quelques exemples stupides.

class StupidExample1 {
    public static void main(String[] args) throws IOException {
        try {
            throw new IOException();
        } catch (IOException e) {
            throw new IOException(new IOException(e));
        }
    }
}

se traduit par une trace de pile verbeuse comme

Exception in thread "main" Java.io.IOException: Java.io.IOException: Java.io.IOException
    at StupidExample1.main(XXX.Java:XX)
Caused by: Java.io.IOException: Java.io.IOException
    ... 1 more
Caused by: Java.io.IOException
    at StupidExample1.main(XXX.Java:XX)

qui peut (et devrait) être effectivement réduit à

Exception in thread "main" Java.io.IOException
    at StupidExample1.main(XXX.Java:XX)

Un autre:

class StupidExample2 {
    public static void main(String[] args) {
        takeString(new String(new String("myString")));
    }

    static void takeString(String s) { }
}

Il est évident que new String(new String("myString")) est une version verbeuse de "myString" Et devrait être refactorisée à ce dernier.

47
Andrew Tobilko
catch (IOException e) {
    throw e;
}

Vous verrez l'exception d'origine uniquement avec la trace de pile d'origine. Vous ne verrez pas cette ligne "rethrow" dans le stacktrace donc c'est un peu transparent.

catch (IOException e) {
    throw new IllegalStateException(e);
}

Vous verrez créé IllegalStateException et son stacktrace avec les informations d'exception et stacktrace "provoquées par". Vous définissez (sur le point d'être) l'exception levée comme cause du IOException nouvellement créé. La couche supérieure verra IllegalStateException et il sera possible d'attraper (vous n'attraperez pas cette exception de cause de capture).

catch (IOException e) {
     throw new IOException();
}

Vous ne verrez que la trace de pile actuelle de la création IOException, sans la cause ajoutée.

14
Antoniossss

Eh bien, en gros, throw e will "rethrow" toutes les valeurs d'origine - également un certain flux de code, qui devrait être masqué, par exemple, pour des raisons de sécurité par exemple. Si vous voulez recréer l'exception, vous obtiendrez - ou vous pouvez obtenir - une autre trace de pile à la place.

Donc, je dirais que vous avez la possibilité de masquer certaines données (je ne sais pas, vous pouvez par exemple enregistrer des exceptions dans un journal spécial, mais vous voudrez transmettre d'autres données de diagnostic à l'utilisateur final).

Vérifions un peu ce qui suit:

  • J'ai créé une classe en tant que simple générateur d'exceptions
  • une autre classe permet de relancer ou recréer une exception
  • après, j'imprime juste le stacktrace et compare les résultats

Générateur d'exceptions

public class ExceptionsThrow {
    public static void throwNewException() throws Exception {
        throw new Exception("originally thrown message");
    }
}

Classe pour repousser/recréer des exceptions

  public class Exceptions {

        public static void reThrowException() throws Exception {
            try {
                ExceptionsThrow.throwNewException();
            } catch (Exception e) {
                throw e;
            }
        }

        public static void reCreateNewException() throws Exception {
            try {
                ExceptionsThrow.throwNewException();
            } catch (Exception e) {
                throw new Exception(e);
            }
        }
    }

Exemple de code de test:

try {
     Exceptions.reThrowException();
} catch (Exception e) {
    System.out.println("1st RETHROW");
    e.printStackTrace();
    System.out.println("===========");
}

try {
    Exceptions.reCreateNewException();
} catch (Exception e) {
    System.out.println("2nd RECREATE");
    e.printStackTrace();
    System.out.println("===========");
}

Et la sortie enfin:

1st RETHROW
Java.lang.Exception: originally thrown message
    at test.main.stackoverflow.ExceptionsThrow.throwNewException(ExceptionsThrow.Java:5)
    at test.main.stackoverflow.Exceptions.reThrowException(Exceptions.Java:7)
    at test.main.MainTest.main(MainTest.Java:110)
Java.lang.Exception: Java.lang.Exception: originally thrown message===========
2nd RECREATE

    at test.main.stackoverflow.Exceptions.reCreateNewException(Exceptions.Java:17)
    at test.main.MainTest.main(MainTest.Java:118)
Caused by: Java.lang.Exception: originally thrown message
    at test.main.stackoverflow.ExceptionsThrow.throwNewException(ExceptionsThrow.Java:5)
    at test.main.stackoverflow.Exceptions.reCreateNewException(Exceptions.Java:15)
    ... 1 more
===========

Dans ce cas, vous pouvez voir principalement les mêmes données, mais certaines supplémentaires, vous pouvez voir le message d'origine, car j'ai utilisé la même exception pour créer la nouvelle, mais vous n'avez pas besoin de le faire comme ceci, donc vous peut masquer la cause d'origine, ou vous n'avez pas besoin d'exposer la logique de l'application, par exemple, vérifions un autre exemple:

  • Je ne prendrai que la cause de l'exception d'origine, mais elle remplacera les données
  • comme vous pouvez le voir, une exception nouvellement créée ne contient pas la trace de pile complète, comme l'origine

Alors:

public static void maskException() throws Exception {
    try {
        ExceptionsThrow.throwNewException();
    } catch (Exception e) {
        throw new Exception("I will dont tell you",e.getCause());
    }
}

Et le résultat:

===========
3rd mask
Java.lang.Exception: I will don't tell you
    at test.main.stackoverflow.Exceptions.maskException(Exceptions.Java:25)
    at test.main.MainTest.main(MainTest.Java:126)
===========

Donc, je dirais que recréer l'exception avec la même instance est un peu inutile, mais il peut y avoir des cas où vous voulez le faire comme ça - masquez les données, ou un autre cas peut être aussi bien si vous voulez changer le type d'exception - par exemple, d'une exception d'E/S à une exception générique, etc.

Dans le monde réel, je me souviens du problème très fréquent, lorsque certains portails Web géraient des exceptions dans les scripts PHP uniquement par ce cas d'impression, puis généralement, lorsque la connexion avec la base de données était ne fonctionne pas correctement, les chaînes de connexion (y compris l'adresse de la base de données et les informations d'identification en texte brut) étaient visibles dans le navigateur Web, par exemple. :)

3
xxxvodnikxxx

Cet exemple n'a pas beaucoup de sens dans ce contexte, car vous lancez la même exception et ne faites rien d'autre. L'enregistrer au moins aura beaucoup plus de sens. Vous interceptez une exception pour la gérer ou la consigner. Si vous ne pouvez pas le gérer, relancez-le (cas 1) ou passez à autre chose (cas 2).

Cas 1:

public class Main {

    // forced to handle or rethrow again
    public static void main(String[] args) throws IOException {
        read();
    }

    public static void read() throws IOException {
        try {
            readInternal();
        } catch (IOException e) {
            throw e;
        }
    }

    private static void readInternal() throws IOException {
        throw new IOException("Output error");
    }
}

Dans la sortie, vous verrez quelque chose comme:

Exception in thread "main" Java.io.IOException: Output error
    at com.alex.Java.Main.readInternal(Main.Java:26)
    at com.alex.Java.Main.read(Main.Java:19)
    at com.alex.Java.Main.main(Main.Java:14)
**Case 2:**

Le modèle ci-dessous vous permet de modifier le type de l'exception et de conserver également les détails de l'exception d'origine:

try {
   // Some code here
} catch (IOException e) {
    throw new IllegalStateException(e);
}

Ce cas se produit souvent lorsque vous souhaitez remplacer un Checked Exception avec un Unchecked exception en préservant le origine du problème et conserver toutes les informations (ce que l'on appelle le chaînage d'exceptions).

Cas d'utilisation réguliers:

  • Vous ne pouvez pas gérer un Checked Exception et vous ne voulez pas le renvoyer à l'appelant. Le renvoi des exceptions cochées force l'appelant à le gérer. Ce n'est pas ce que vous voulez faire s'il n'y a pas de cas de récupération réguliers.
  • Les exceptions telles que IOException sont rarement utiles au client. Vous devez envoyer quelque chose de plus précis et clair dans le cadre de votre domaine d'activité.

IOException encapsulé dans Unchecked Exception comme DocumentReadException éclairera la situation réelle et ne forcera pas les appelants à la gérer:

public class Main {

    public static void main(String[] args) {
        read();
    }

    public static void read() {
        try {
            readInternal();
        } catch (IOException e) {
            // log and wrap the exception to a specific business exception
            logger.error("Error reading the document", e);
            throw new DocumentReadException(e);
        }
    }

    private static void readInternal() throws IOException {
        throw new IOException("Output error");
    }
}

La sortie sera similaire à:

Exception in thread "main" Java.lang.IllegalArgumentException: Error reading the document
    at com.alex.Java.Main.read(Main.Java:21)
    at com.alex.Java.Main.main(Main.Java:14)
Caused by: Java.io.IOException: Output error
    at com.alex.Java.Main.readInternal(Main.Java:26)
    at com.alex.Java.Main.read(Main.Java:19)
    ... 1 more

Comme vous pouvez le voir sur la trace de la pile, la cause première était un enregistreur pour vous aider à trouver le problème d'origine et l'exception de domaine métier a été envoyée à un utilisateur.

2
J-Alex

Lorsque vous lancez une exception, vous faites quelque chose de très similaire à la création ou à la déclaration d'une instance.

throw edéclare une instance de IOException (attribue un espace mémoire sur le périphérique sans stocker de données), donc vous le renvoyez sur le bloc catch pendant que vous déclarez une exception pré-faite.

throw new IOException einitalise une nouvelle instance de IOException, donc vous créez une nouvelle exception sur le bloc catch car elle ne fait référence à aucune autre exception.

Toutefois

Vous ne lancez généralement pas d'exceptions sur le bloc catch. Dans la plupart des cas, vous devez:

  1. Avertissez le compilateur que vous allez lever une exception sur une méthode (throws IOException Après la déclaration de paramètre)
  2. Lancez l'exception (throw new IOException("Warning text") sur le corps de la méthode) chaque fois que les données introduites ne peuvent pas être validées de toute façon.
  3. Sur les blocs try/catch, placez l'invocation ou l'appel à la méthode sur le bloc try; sur le bloc catch, mettez un message d'erreur.

Un exemple de code ressemblerait à ceci:

public static void myMethod() throws IOException{
int prueba=0;
if(prueba>9 //some condition){
    //do a thing
}else
    throw new IOException("This number is not correct");
}//end of method
try{
    myClass.myMethod();
}catch{
    System.out.println("An error has occurred");
}//end of try/catch

C'est ainsi que j'ai appris à faire correctement les exceptions, mais j'espère avoir répondu à votre question.

Nous devons d'abord comprendre pourquoi créer un nouveau type d'exception pour encapsuler une exception.

Cela peut être utile lorsque vous souhaitez intercepter Java exceptions et le mapper comme un scénario d'erreur spécifique dans votre code.

Par exemple:

try {
  // read file
} catch (IOException e) {
  throw new MyAppFailedToReadFile(e);
}

Ainsi, dans le cas ci-dessus, vous pourrez suivre des erreurs spécifiques dans votre application.
C'est utile lorsque vous avez une classe pour gérer toutes les exceptions dans votre application et les mapper à des messages d'erreur spécifiques.

0
tbraun