web-dev-qa-db-fra.com

levée d'une exception d'exécution dans Java

Je travaille en tant qu'entrepreneur concevant une entreprise Java application pour mon client dans le rôle de responsable technique. L'application sera utilisée par les utilisateurs finaux et il y aura une équipe de support qui soutiendra l'application quand nous partons.

Les autres pistes techniques avec lesquelles je travaille ont l'impression que la gestion des exceptions rendra le code sale. Le système doit lever les exceptions vérifiées uniquement à partir du niveau Service et le reste du code doit lever les exceptions d'exécution de tous les autres niveaux afin qu'il ne soit pas nécessaire de gérer les exceptions non vérifiées.

Quel est le besoin de lever une exception non contrôlée dans une application métier?

D'après mon expérience dans le passé sur les exceptions d'exécution:

1) Les exceptions non vérifiées rendent le code imprévisible car elles n'apparaissent pas même dans Javadoc.

2) Lancer des exceptions non vérifiées dans une application métier est insensé car lorsque vous la lancez et que cela va directement sur le visage des utilisateurs, comment l'expliquez-vous à l'utilisateur? J'ai vu suffisamment d'applications Web qui montrent 500 -Internal Error. Contact Administrator ce qui ne signifie rien pour un utilisateur final ou pour l'équipe de support qui gère l'application.

3) Le lancement d'exceptions d'exécution oblige les utilisateurs de la classe qui lancent l'exception à parcourir le code source pour déboguer et voir pourquoi l'exception est levée. Cela ne peut être évité que si le Javadoc de l'exception d'exécution se trouve être parfaitement documenté, ce qui, je trouve, n'est jamais un cas.

Les exceptions vérifiées sont un expriment échoué dans la conception du langage. Ils vous obligent à avoir des abstractions extrêmement fuyantes et un code sale. Ils doivent être évités autant que possible.

Quant à vos points:

1) Les exceptions vérifiées rendent le code sale et non moins imprévisible car elles apparaissent partout.

2) Comment une exception vérifiée est-elle mieux affichée à l'utilisateur? La seule vraie différence entre les exceptions vérifiées et non contrôlées est technique et n'affecte que le code source.

3) Avez-vous déjà entendu parler de traces de pile? Ils vous indiquent exactement où l'exception a été levée, qu'elle soit cochée ou décochée. En fait, les exceptions vérifiées ont tendance à être pires pour le débogage, car elles sont souvent encapsulées, ce qui conduit à des traces de pile plus longues et plus laides, ou même perdues ensemble parce que l'encapsulation a été mal effectuée.

Il existe deux types d'exceptions: celles qui se produisent "normalement" et sont généralement gérées très près de l'endroit où elles se produisent, et celles qui sont vraiment exceptionnelles et peuvent être gérées de manière générique dans une couche très élevée (il suffit d'interrompre l'action en cours et de la consigner/afficher une erreur).

Les exceptions vérifiées étaient une tentative de mettre cette distinction dans la syntaxe du langage au moment où les exceptions sont définies. Les problèmes avec cela sont

  • La distinction appartient vraiment au appelant, pas au code qui lève l'exception
  • C'est complètement orthogonal à la signification sémantique d'une exception, mais le lier à la hiérarchie des classes vous oblige à mélanger les deux
  • Le seul point des exceptions est que vous pouvez décider à quel niveau les attraper sans risquer de perdre une erreur silencieusement ou d'avoir à polluer le code à des niveaux intermédiaires ou; les exceptions vérifiées perdent ce deuxième avantage.
13
Michael Borgwardt

À mon avis, le type d'exception levée dépend de ce que fait votre code.

Je jette des exceptions vérifiées quand je m'attends à ce qu'elles se produisent assez souvent (les utilisateurs saisissent des données douteuses par exemple) et je m'attends à ce que le code appelant gère la situation.

Je lance des exceptions non vérifiées/d'exécution (pas aussi souvent que vérifiées) lorsque j'ai une situation rare que je ne m'attends pas à ce que le code appelant gère. Un exemple pourrait être une sorte d'erreur bizarre liée à la mémoire que je ne m'attends jamais à se produire. Les types d'erreurs que vous prévoyez de supprimer l'application sont décochés.

Aucune exception ne doit apparaître devant un utilisateur sans un certain niveau de support. Même si c'est juste un "s'il vous plaît couper et coller ce vidage d'erreur dans un e-mail". Rien n'est plus ennuyeux pour un utilisateur que de se faire dire qu'il y a une erreur, mais étant donné qu'aucun détail ou action qu'il ne peut entreprendre pour l'initier n'est corrigé.

Je suppose que la philosophie que vous mentionnez provient de l'une des deux sources suivantes:

  • Programmeurs paresseux essayant d'éviter de faire du travail.
  • Ou des développeurs qui ont dû prendre en charge du code qui est allé dans l'autre sens. c'est-à-dire sur-gestion des erreurs. Le type de code qui contient de grandes quantités de gestion des exceptions, dont une grande partie ne fait rien, ou pire encore, est utilisé pour le contrôle de flux et à d'autres fins incorrectes.
2
drekka

Si vous ne voulez pas d'exceptions d'exécution non contrôlées, alors pourquoi utilisez-vous java? Il existe la possibilité d'exceptions d'exécution presque partout - notamment les exceptions NullPointerException, ArithmeticException, ArrayIndexOutOfBounds, etc.

Et lorsque vous lirez une fois les fichiers journaux d'un système J2EE, comme, par exemple, une installation SAP NetWeaver, vous verrez que de telles exceptions se produisent littéralement tout le temps.

1
Ingo

Il y a quelques règles de gestion des exceptions que vous devez garder à l'esprit. Mais d'abord, vous devez vous rappeler que les exceptions font partie de l'interface exposée par le code; les documenter . Ceci est particulièrement important lorsque l'interface est publique, bien sûr, mais c'est également une très bonne idée dans les interfaces privées.

Les exceptions ne doivent être gérées qu'au point où le code peut faire quelque chose de sensé avec elles. La pire option de manipulation est de ne rien faire du tout, ce qui ne devrait être fait que lorsque c'est exactement la bonne option. (Lorsque j'ai une telle situation dans mon code, j'inclus un commentaire à cet effet afin que je sache ne pas me soucier du corps vide.)

La deuxième pire option consiste à lever une exception sans rapport avec l'original attaché comme cause. Le problème ici est que les informations dans l'exception d'origine qui permettraient de diagnostiquer le problème sont perdues; vous créez quelque chose avec lequel personne ne peut rien faire (à part se plaindre que "cela ne fonctionne pas", et nous savons tous comment nous détestons ces rapports de bogues).

Il est beaucoup mieux de consigner l'exception. Cela permet à quelqu'un de découvrir le problème et de le résoudre, mais vous ne devez enregistrer l'exception qu'au point où elle serait sinon perdue ou signalée via une connexion externe. Ce n'est pas parce que la journalisation plus souvent est un problème majeur en tant que tel, mais plutôt parce qu'une journalisation excessive signifie que le journal consomme plus d'espace sans contenir plus d'informations. Une fois que vous avez enregistré l'exception, vous pouvez signaler un précis à l'utilisateur/client en toute bonne conscience (tant que vous incluez également l'heure de génération - ou autre identifiant de corrélation - dans ce rapport afin que la version courte puisse être mise en correspondance avec les détails si nécessaire).

La meilleure option est, bien sûr, de gérer complètement l'exception, en traitant la situation d'erreur dans son intégralité. Si vous pouvez le faire, faites-le! Cela peut même signifier que vous pouvez éviter d'avoir à enregistrer l'exception.

Une façon de gérer une exception consiste à lever une autre exception qui fournit une description de niveau supérieur du problème (par exemple, "failed to initialize" au lieu de "index out of bounds ”). C'est un bon modèle tant que vous ne perdez pas les informations sur la cause de l'exception; utilisez l'exception détaillée pour initialiser le cause de l'exception de niveau supérieur ou enregistrez le détail (comme expliqué ci-dessus). La journalisation est plus appropriée lorsque vous êtes sur le point de traverser une frontière inter-processus, comme un appel IPC, car il n'y a aucune garantie que la classe d'exception de bas niveau sera présente du tout sur le l'autre extrémité de la connexion. Le maintien en tant que cause associée est le plus approprié lors du franchissement d'une limite interne.

Un autre modèle que vous voyez est le catch-and-release:

try {
    // ...
} catch (FooException e) {
    throw e;
}

Il s'agit d'un anti-modèle sauf si vous avez des contraintes de type provenant d'autres clauses catch ce qui signifie que vous ne pouvez pas simplement laisser l'exception passer par lui-même. Ensuite, ce n'est qu'une laideur de Java.

Il n'y a pas de réelle différence entre les exceptions vérifiées et celles non vérifiées, à part le fait que vous devez déclarer les exceptions vérifiées qui franchissent les limites des méthodes. C'est toujours une bonne idée de documenter les exceptions non contrôlées (avec le @throws javadoc comment) si vous savez qu'ils sont délibérément lancés par votre code. Ne jetez pas délibérément Java.lang.Error ou ses sous-classes (sauf si vous écrivez une implémentation JVM).

Opinion: Un cas d'erreur inattendu représente toujours un bogue dans votre code. Les exceptions vérifiées sont un moyen de gérer cette menace, et lorsque les développeurs utilisent délibérément des exceptions non contrôlées pour éviter les problèmes de gestion des cas d'erreur, vous accumulez beaucoup de dettes techniques que vous devrez nettoyer un certain temps si vous voulez du code robuste. La gestion des erreurs bâclée n'est pas professionnelle (et regarder la gestion des erreurs est un bon moyen de déterminer à quel point un programmeur est vraiment bon).

1
Donal Fellows

Je pense que vous ne devez lever l'exception vérifiée que lorsque l'application peut récupérer. Ainsi, les utilisateurs de votre bibliothèque doivent intercepter ces exceptions et faire ce dont ils ont besoin pour récupérer. Tout le reste doit être décoché.

Par exemple,

Si votre application charge des données à partir d'un fichier ou d'une base de données. Alors,..

try {
  File data = new File(...);
  // parse file here
} catch (Exception ex) {
  throw new MissingDataFileException("data file not found");
}

l'appelant peut toujours intercepter l'exception MissingDataFileException vérifiée, puis essayer de charger les données à partir de la base de données.

try {
  Connection con = DriverManager.getConnection( Host, username, password );
  // query data here
} catch (Exception ex) {
  throw new RuntimeException("better call Saul!");
}
0
Hendrix