Pourquoi la catch(Exception)
est-elle presque toujours une mauvaise idée?
Parce que lorsque vous attrapez une exception vous êtes censé la gérer correctement . Et vous ne pouvez pas vous attendre à gérer toutes sortes d'exceptions dans votre code. De même, lorsque vous interceptez toutes les exceptions, vous pouvez obtenir une exception qui ne peut pas gérer et empêcher le code situé en haut de la pile de le gérer correctement.
Le principe général est d’attraper le type le plus spécifique possible.
Petite histoire: ça s'appelle le masquage des bogues. Si vous avez un morceau de code qui ne fonctionne pas bien et que vous lancez des exceptions (ou si vous transmettez une entrée mal formée à ce morceau de code) et que vous aveuglez les yeux en détectant toutes les exceptions possibles, vous ne découvrirez jamais le bogue et ne le corrigerez pas.
Vous ne devriez intercepter des exceptions que si vous pouvez les gérer correctement. Comme vous ne pouvez pas gérer correctement toutes les exceptions possibles, vous ne devriez pas les attraper :-)
Parce que vous ne savez pas vraiment pourquoi une exception s'est produite, et plusieurs exceptions nécessitent le traitement correct d'une voiture très spéciale (si possible), comme une exception OutOfMemoryException et des exceptions système de bas niveau similaires.
Par conséquent, vous ne devriez intercepter que des exceptions:
Cela dépend de ce dont vous avez besoin. Si vous devez gérer différents types d'exceptions de différentes manières, vous devez utiliser plusieurs blocs catch et capturer autant d'exceptions spécifiques que possible.
Mais parfois, vous devrez peut-être gérer toutes les exceptions de la même manière. Dans de tels cas, catch (Exception) peut être correct. Par exemple:
try
{
DoSomething();
}
catch (Exception e)
{
LogError(e);
ShowErrorMessage(e); // Show "unexpected error ocurred" error message for user.
}
Je trouve deux utilisations acceptables de catch(Exception)
:
Le premier cas est explicite, mais laissez-moi développer le second:
Faire:
try {
// xxxx
} catch(Exception e) {
logger.error("Error XXX",e)
}
est le masquage de bugs comme @dimitarvp dit.
Mais le ci-dessous est différent:
try {
// xxxx
} catch(Exception e) {
throw new BussinessException("Error doing operation XXX",e)
}
De cette façon, vous ne négligez pas les insectes et ne les cachez pas sous le tapis. Vous fournissez une exception de haut niveau avec un message plus explicatif aux couches d'application supérieures.
Il est également toujours important de gérer les exceptions au niveau correct. Si vous transmettez une exception de bas niveau à une couche à niveau élevé, il est pratiquement impossible pour la couche supérieure de la gérer correctement.
Dans ce cas, je préfère masquer les exceptions de bas niveau avec un autre qui fournit un contexte et un message plus précis, ainsi que l'exception originale permettant de rentrer dans les détails.
Néanmoins, si vous pouvez intercepter des exceptions plus concrètes et leur fournir un meilleur traitement, vous devez le faire.
Si dans un bloc de code, vous pouvez obtenir un SQLException
et un NetworkException
, vous devez les intercepter et fournir des messages et un traitement appropriés pour chacun d'eux . Mais si à la fin du bloc try/catch vous avez un Exception
qui le mappe BussinessException
c'est correct pour moi . En fait, je le trouve adéquat lorsque des couches de service plus élevées ne lèvent que des exceptions métier (avec des détails à l'intérieur).
Outre ce à quoi @anthares a encore répondu:
Parce que lorsque vous attrapez une exception vous êtes censé la gérer correctement . Et vous ne pouvez pas vous attendre à gérer toutes sortes d'exceptions dans votre code. De même, lorsque vous interceptez toutes les exceptions, vous pouvez obtenir une exception qui ne peut pas gérer et empêcher le code situé en haut de la pile de le gérer correctement.
Le principe général est d’attraper le type le plus spécifique possible.
catch(Exception)
est une mauvaise pratique car elle intercepte également toutes les RuntimeException (exceptions non vérifiées).
Mais parfois ça va! Par exemple, si vous avez un élément de code qui fait quelque chose de «supplémentaire», qui ne vous intéresse vraiment pas, et que vous ne voulez pas que votre application explose. Par exemple, j'ai récemment travaillé sur une grande application dans laquelle nos partenaires commerciaux souhaitaient qu'une transaction quotidienne soit résumée dans un nouveau fichier journal. Ils ont expliqué que le journal n'était pas très important pour eux et qu'il n'était pas considéré comme une exigence. C'était simplement un élément supplémentaire qui les aiderait à comprendre les données traitées. Ils n'en avaient pas besoin, car ils pouvaient obtenir des informations ailleurs. Il s'agit donc d'un cas rare où il est parfaitement correct d'attraper et d'avaler des exceptions.
J'ai également travaillé dans une société où tous les objets Throwables ont été capturés, puis rediffusés dans une exception RuntimeException personnalisée. Je ne recommanderais pas cette approche, mais simplement en soulignant que c'est fait.
Cela peut être spécifique à Java:
Parfois, vous aurez besoin d'appeler des méthodes qui jettent des exceptions vérifiées. Si cela se trouve dans votre couche logique EJB/Business, vous avez 2 choix - capturez-les ou relancez-les.
La capture de classes d'exceptions spécifiques signifie que vous devrez ré-analyser vos actions pour lesquelles des exceptions peuvent être levées lorsque vous regardez comment ce code traite les exceptions. Vous vous retrouverez souvent dans une situation difficile et il peut être très difficile de déterminer si les exceptions sont gérées correctement.
Re-lancer signifie que le code appelant vos EJB sera jonché de code de capture qui ne signifiera généralement rien pour la classe appelante. nb Si vous lancez des exceptions vérifiées à partir de méthodes EJB, vous serez responsable de l'annulation manuelle des transactions.
N’est-ce pas un autre scénario valable de s’assurer qu’un thread garde toujours une exception capturante en son sein?
Thread shouldRunWhenApplicationRuns = new Thread() {
@Override
public void run() {
try {
// do something that should never end
} catch (Exception ex) {
// log it
}
};
shouldRunWhenApplicationRuns.start();