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);
}
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.
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.
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:
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:
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. :)
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:
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.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.
Lorsque vous lancez une exception, vous faites quelque chose de très similaire à la création ou à la déclaration d'une instance.
throw e
dé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 e
initalise 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.
Vous ne lancez généralement pas d'exceptions sur le bloc catch. Dans la plupart des cas, vous devez:
throws IOException
Après la déclaration de paramètre)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.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.