web-dev-qa-db-fra.com

Différence entre les lancers dans la signature de méthode et les instructions de lancer dans Java

J'essaie de clarifier la différence entre jette dans la signature de la méthode et jette les déclarations en Java. La signature de méthode est la suivante:

public void aMethod() throws IOException{
    FileReader f = new FileReader("notExist.txt");
}

Lancer des déclarations est le suivant:

public void bMethod() {
    throw new IOException();
}

D'après ma compréhension, un throws dans la signature de la méthode est une notification que la méthode peut lever une telle exception. throw est ce qui jette réellement un objet créé dans des circonstances appropriées. Dans ce sens, throw dans la signature de la méthode doit toujours apparaître s'il existe une instruction throw dans la méthode.

Cependant, le code suivant ne semble pas le faire. Le code provient de la bibliothèque. Ma question est pourquoi cela se produit? Suis-je en train de mal comprendre les concepts?

Ce morceau de code est une copie de Java.util.linkedList. @auteur Josh Bloch

 /**
 * Returns the first element in this list.
 *
 * @return the first element in this list
 * @throws NoSuchElementException if this list is empty
 */
public E getFirst() {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return f.item;
}

Mise à jour sur la réponse:

mise à jour 1: le code ci-dessus est-il le même que le suivant?

// as far as I know, it is the same as without throws
public E getFirst() throws NoSuchElementException {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return f.item;
}

mise à jour 2: pour l'exception vérifiée. Dois-je avoir des "jets" dans la signature? Oui.

// has to throw checked exception otherwise compile error
public String abc() throws IOException{
    throw new IOException();
}
33
Weishi Zeng

Vous avez à peu près raison. Sauf pour une chose que je mentionnerai un peu.

jette fait autant partie de l'API de la méthode que le nom et les paramètres. Les clients savent que s'ils appellent cette méthode, ils doivent gérer cette exception - simplement en la lançant également ou en l'attrapant et en la manipulant (ce qui peut en fait entraîner le lancement d'une autre exception enveloppant l'original). jette est adressé au moment de la compilation.

throw est l'acte réel de faire savoir au runtime que quelque chose de mauvais s'est produit - que la condition exceptionnelle qui nous inquiétait s'est en fait produite. Il doit donc être traité lors de l'exécution.

Mais vous n'aviez pas tout à fait raison lorsque vous avez dit: "La signature de méthode Throws doit toujours apparaître s'il existe une instruction throw dans la méthode. C'est souvent vrai mais pas toujours. Je pourrais également appeler une autre méthode qui lève une exception dans ma méthode, et si je ne l'attrape pas, ma méthode doit la lancer. Dans ce cas, il n'y a pas de rejet explicite de la même exception par moi.

Le dernier point est que vous avez seulement besoin de déclarer une exception dans jette lorsque l'exception est une vérifié exception - ce qui signifie qu'elle est de l'autre côté de la Hiérarchie des classes d'exception de RuntimeException. Les exceptions vérifiées courantes sont IOException et SQLException. Les exceptions vérifiées doivent être répertoriées dans la partie jets de la signature de la méthode si vous ne les gérez pas vous-même. Tout ce qui sous-classe RuntimeException - comme NoSuchElementException dans votre exemple et aussi le NullPointerException détesté - est une exception non vérifiée et n'a pas besoin d'être attrapé ou levé ou quoi que ce soit.

En règle générale, vous utilisez des exceptions vérifiées pour les problèmes récupérables (où le client sait ce qui peut se produire et peut gérer le problème avec élégance et passer à autre chose) et des exceptions non vérifiées pour les problèmes catastrophiques (comme ne peut pas se connecter à la base de données).

Si vous pouvez passer outre toutes les choses AOP, this est une excellente discussion sur la façon dont vous utilisez efficacement les exceptions vérifiées et non contrôlées.

29
Vidya

Vidya a fourni une excellente réponse à vos questions.

Les mots les plus importants sont "Le dernier point est que vous n'avez besoin de déclarer une exception dans les lancers que lorsque l'exception est une exception vérifiée"

Juste pour vous montrer un exemple de code, qu'est-ce que cela signifie? Imaginez que nous aimerions utiliser FileOutputStream afin de transmettre certaines données. La fonction ressemblerait à ceci:

public void saveSomeData() throws IOException {
  FileInputStream in = null;
  FileOutputStream out = null;

  try {
    in = new FileInputStream("input.txt");
    out = new FileOutputStream("output.txt");
    int c;

    while ((c = out.read() != -1) {
      in.write(c);
    }
  } catch (Exception e) {
    e.printStackTrace();
  } finally {
    // Close in
    if (in != null) {
      in.close(); // <-- If something bad happens here it will cause runtime error!
    }
    // Close out
    ...
  }
}

Imaginez maintenant, si vous ne fournissiez pas lance IOException et que quelque chose de mauvais se produit à l'intérieur de l'instruction finalement {} - cela provoquerait une erreur.

5
0leg

L'attribut throw dans la signature de la méthode, comme vous l'avez bien deviné, indique au compilateur que la méthode lève une exception qui doit être interceptée par l'appelant. Ce type d'exception, à savoir appelé exception vérifiée est quelque chose que l'appelant DOIT toujours attraper ou renvoyer à son appelant. C'est quelque chose au niveau du compilateur, la signature spécifie quelle exception la méthode est capable de lancer: cela applique un try-catch ou re-répartition dans l'appelant et une instruction throw quelque part à l'intérieur de la méthode, est une contrainte que le développeur place pour spécifier quelque chose sur le comportement de la méthode.

D'autre part, d'autres exceptions, à savoir non vérifiées ou exceptions d'exécution , ( NoSucheElementException en est un exemple) sont des exceptions que vous n'êtes pas obligé de spécifier car elles proviennent de situations différentes.

La différence conceptuelle est que l'exception vérifiée est généralement utilisée pour avertir d'une situation exceptionnelle qui devrait être gérée d'une manière ou d'une autre (pensez à IOException) par le développeur, tandis que non vérifiée sont de vraies erreurs (comme NullPointerException ou comme dans votre exemple NoSuchElementException)

3
Jack

RuntimeExceptions n'ont pas à être manipulés dans le bloc try-catch donc ils n'ont pas à être déclarés comme levés et NoSuchElementException est RuntimeException car il l'étend.

2
Pshemo