web-dev-qa-db-fra.com

Quand attraper l'exception et quand lancer les exceptions?

Je code en Java depuis un moment maintenant. Mais parfois, je ne comprends pas quand je devrais lancer l'exception et quand devrais-je attraper l'exception. Je travaille sur un projet dans lequel il y a beaucoup de méthodes. La hiérarchie est quelque chose comme ceci-

Method A will call Method B and Method B will call some Method C and Method C will call Method D and Method E.

Donc, actuellement, je fais des exceptions dans toutes les méthodes et je les intercepte dans la méthode A, puis je consignés comme une erreur.

Mais je ne suis pas sûr que ce soit la bonne façon de le faire? Ou devrais-je commencer à attraper des exceptions dans toutes les méthodes. C’est la raison pour laquelle cette confusion a commencé dans ma question: quand devrais-je saisir l’exception et quand devrais-je lancer les exceptions? Je sais que c'est une question idiote, mais j'ai du mal à comprendre ce concept majeur.

Quelqu'un peut-il me donner un exemple détaillé de When to catch the Exception vs When to throw the Exceptions afin que mes concepts soient clarifiés à ce sujet? Et dans mon cas, devrais-je continuer à lancer l'exception et l'attraper ensuite dans la méthode d'appel principale A?

35
AKIWEB

Vous devriez attraper l'exception quand vous êtes dans la méthode qui sait quoi faire.

Par exemple, oubliez comment cela fonctionne réellement pour le moment, disons que vous écrivez une bibliothèque pour ouvrir et lire des fichiers.

Donc, vous avez un cours, dites:

public class FileInputStream extends InputStream {
    public FileInputStream(String filename) { }
}

Maintenant, disons que le fichier n'existe pas. Que devrais tu faire? Si vous avez du mal à trouver la réponse, c'est parce qu'il n'y en a pas ... le FileInputStream ne sait pas quoi faire à propos de ce problème. Donc, il jette la chaîne, c'est à dire:

public class FileInputStream extends InputStream {
    public FileInputStream(String filename) throws FileNotFoundException { }
}

Maintenant, disons que quelqu'un utilise votre bibliothèque. Ils pourraient avoir un code qui ressemble à ceci:

public class Main {
    public static void main(String... args) {
        String filename = "foo.txt";
        try {
            FileInputStream fs = new FileInputStream(filename);

            // The rest of the code
        } catch (FileNotFoundException e) {
            System.err.println("Unable to find input file: " + filename);
            System.err.println("Terminating...");
            System.exit(3);
        }
    }
}

Ici, le programmeur sait quoi faire, alors il attrape l'exception et la gère.

48
durron597

Je vais partager un modèle qui a sauvé mon bacon dans un ou deux environnements de production.

Motivation

Mon but est de faire en sorte que le pauvre type (peut-être moi) qui essaie de résoudre un ticket d’aide de plusieurs utilisateurs obtienne une belle hiérarchie d’erreurs 'causées par' à suivre, complétées par des données telles que tout cela sans encombrer le code.

Méthode

Pour y parvenir, j'attrape toutes les exceptions vérifiées et les rejette comme des exceptions non vérifiées. J'utilise ensuite une capture globale à la limite de chacune de mes couches architecturales (généralement abstraite ou injectée, elle n'est donc écrite qu'une seule fois). C'est à ces moments-là que je peux ajouter un contexte supplémentaire à la pile d'erreurs, ou décider s'il convient de se connecter et de l'ignorer, ou de générer une exception vérifiée personnalisée avec des variables pour contenir tout contexte supplémentaire. D'un autre côté, je ne consigne que les erreurs de la couche supérieure pour empêcher la «double journalisation» (par exemple, le travail cron, le contrôleur de ressort pour ajax)

throw new RuntimeException(checked,"Could not retrieve contact " + id);

Avec cette approche, les signatures de méthodes de votre interface utilisateur graphique ou de votre entreprise ne doivent pas être encombrées du fait qu'elles doivent déclarer des "lancers" pour les exceptions liées à la base de données.

Un exemple de la façon dont cela fonctionne dans la vie réelle:

Disons que le travail de mon code est un processus automatisé de renouvellement de nombreuses polices d'assurance. L'architecture prend en charge une interface graphique pour déclencher manuellement le renouvellement d'une stratégie. Disons également que le code postal de la zone d'évaluation est corrompu dans la base de données pour l'une de ces règles.

Un exemple du type de journal des erreurs que je voudrais atteindre serait.

Message de journal: Stratégie de signalisation 1234 pour une intervention manuelle due à une erreur:

À partir de la trace de la pile: politique de renouvellement des erreurs 1234. Annulation de la transaction ... Cette capture couvrirait également les erreurs telles que les erreurs de sauvegarde ou la génération d'une lettre.

À partir de la trace de la pile: causée par: stratégie de tarification des erreurs 1234 ... Cette capture capturerait les erreurs lors de l'extraction de nombreux autres objets, ainsi que les erreurs d'algorithme telles que NPE, etc. ...

Depuis la trace de la pile: causée par: une erreur lors de l'extraction de la zone d'évaluation 73932 ...

Depuis la trace de la pile: Causée par: JPA: null inattendu dans le champ 'postcode' 

5
Tinman

En général, attrapez au niveau où vous pouvez faire quelque chose d’utile à ce sujet. Par exemple, l'utilisateur essaie de se connecter à une base de données et la méthode D échoue.

Comment voulez-vous le gérer? Peut-être en mettant en place un dialogue disant "Désolé, impossible de se connecter à SERVER/DB" ou autre chose. Est-ce que la méthode A, B ou C qui a créé cette information SERVEUR/DB (par exemple, en lisant un fichier de paramètres ou en demandant une entrée utilisateur) et ayant essayé la connexion? C'est probablement la méthode qui devrait gérer l'exception. Ou au moins 1 loin de la méthode qui devrait le gérer.

Cela varie vraiment en fonction de votre application, il ne peut donc s'agir que d'un conseil très général. La plupart de mon expérience concerne les applications de bureau/Swing, et vous pouvez généralement avoir une idée des classes qui exécutent la logique de programme (par exemple, "Contrôleur") et des personnes qui placent des boîtes de dialogue (par exemple, "Afficher"). Habituellement, le "contrôleur" devrait intercepter l'exception et essayer de faire quelque chose.

Dans une application Web, cela peut être différent.

Certains codes très squelettiques, la plupart des classes n’existent pas, et je ne suis pas sûr qu’une adresse URL pour la base de données ait un sens, mais vous avez l’idée. Vaguement Swingish ...

/*  gets called by an actionListener when user clicks a menu etc... */
public URL openTheDB() {
  URL urlForTheDB = MyCoolDialogUtils.getMeAURL(URL somePreviousOneToFillInTheStart);
  try {
     verifyDBExists(urlForTheDB);
     // this may call a bunch of deep nested calls that all can throw exceptions
     // let them trickle up to here

     // if it succeeded, return the URL
     return urlForTheDB;
  }
  catch (NoDBExeption ndbe) {
    String message = "Sorry, the DB does not exist at " + URL;
    boolean tryAgain = MyCoolDialogUtils.error(message);
    if (tryAgain)
      return openTheDB();
    else
      return null;  // user said cancel...
  }
  catch (IOException joe) {
    // maybe the network is down, aliens have landed
    // create a reasonable message and show a dialog
  }

}
4
user949300

Vous devez gérer l'exception au niveau le plus bas possible. Si method ne peut pas gérer correctement l'exception, vous devriez la lancer.

  • catch Si vous avez une méthode qui se connecte à une ressource (par exemple, ouvre un fichier/réseau)
  • jeter si la classe supérieure dans la hiérarchie a besoin d'informations sur l'erreur
3
Marcin Szymczak

Vous lève généralement une exception lorsque vous souhaitez informer l'appelant de la méthode de certains échecs.

par exemple, entrée utilisateur non valide, problèmes de base de données, pannes de réseau, fichiers absents

1
Sajan Chandran

Comme d’autres l’ont dit, en règle générale, vous devriez capturer une exception lorsque vous pouvez réellement la gérer, sinon, jetez-la.

Par exemple, si vous écrivez du code qui lit des informations sur un lecteur connecté à partir d'un fichier de sauvegarde et que l'une de vos méthodes d'E/S génère une variable IOException, vous voudrez alors lancer cette exception et le code qui appelle la méthode load intercepter cette exception et la gérer en conséquence (par exemple, déconnecter le lecteur, envoyer une réponse au client, etc.). La raison pour laquelle vous ne voudriez pas gérer l'exception dans la méthode load est que, dans la méthode, vous ne pouvez pas gérer l'exception de manière significative. Vous devez donc déléguer l'exception à l'appelant dans l'espoir qu'il puisse la gérer.

0
Martin Tuskevicius