Questions d’entrevue Java Exceptions (Réponses)

Questions d'entretiens avec Java Exceptions (+ Réponses)

1. Vue d'ensemble

Les exceptions sont un sujet essentiel que tous les développeurs Java devraient connaître. Cet article fournit des réponses à certaines des questions pouvant apparaître lors d'une interview.

2. Des questions

Q1. Qu'est-ce qu'une exception?

Une exception est un événement anormal qui se produit pendant l’exécution d’un programme et perturbe le flux normal des instructions du programme.

Q2. Quel est le but des mots-clés Throw and Throws?

Le mot-cléthrows est utilisé pour spécifier qu'une méthode peut lever une exception lors de son exécution. Il applique la gestion des exceptions explicites lors de l'appel d'une méthode:

public void simpleMethod() throws Exception {
    // ...
}

Le mot cléthrow nous permet de lancer un objet d'exception pour interrompre le déroulement normal du programme. Ceci est le plus couramment utilisé lorsqu'un programme ne remplit pas une condition donnée:

if (task.isTooComplicated()) {
    throw new TooComplicatedException("The task is too complicated");
}

Q3. Comment gérer une exception?

En utilisant une instructiontry-catch-finally:

try {
    // ...
} catch (ExceptionType1 ex) {
    // ...
} catch (ExceptionType2 ex) {
    // ...
} finally {
    // ...
}

Le bloc de code dans lequel une exception peut se produire est inclus dans un bloctry. Ce bloc est également appelé code "protégé" ou "protégé".

Si une exception se produit, le bloccatch qui correspond à l'exception levée est exécuté, sinon tous les blocscatch sont ignorés.

Le blocfinally est toujours exécuté après la sortie du bloctry, qu'une exception ait été levée ou non à l'intérieur.

Q4. Comment pouvez-vous attraper plusieurs exceptions?

Il existe trois façons de gérer plusieurs exceptions dans un bloc de code.

La première consiste à utiliser un bloccatch qui peut gérer tous les types d'exceptions levées:

try {
    // ...
} catch (Exception ex) {
    // ...
}

N'oubliez pas que la pratique recommandée consiste à utiliser des gestionnaires d'exception aussi précis que possible.

Les gestionnaires d'exceptions qui sont trop larges peuvent rendre votre code plus sujet aux erreurs, intercepter les exceptions qui n'étaient pas prévues et provoquer un comportement inattendu dans votre programme.

La deuxième méthode consiste à implémenter plusieurs blocs catch:

try {
    // ...
} catch (FileNotFoundException ex) {
    // ...
} catch (EOFException ex) {
    // ...
}

Notez que, si les exceptions ont une relation d'héritage; le type enfant doit venir en premier et le type parent plus tard. Si nous ne le faisons pas, cela entraînera une erreur de compilation.

La troisième consiste à utiliser un bloc multi-captures:

try {
    // ...
} catch (FileNotFoundException | EOFException ex) {
    // ...
}

Cette fonctionnalité, introduite pour la première fois dans Java 7; réduit la duplication de code et facilite la maintenance.

Q5. Quelle est la différence entre une exception cochée et une exception non cochée?

Une exception vérifiée doit être traitée dans un bloctry-catch ou déclarée dans une clausethrows; alors qu'une exception non vérifiée ne doit pas être gérée ni déclarée.

Les exceptions cochées et non cochées sont également appelées exceptions à la compilation et à l'exécution, respectivement.

Toutes les exceptions sont des exceptions vérifiées, à l'exception de celles indiquées parError,RuntimeException et leurs sous-classes.

Q6. Quelle est la différence entre une exception et une erreur?

Une exception est un événement qui représente une condition à partir de laquelle il est possible de récupérer, tandis qu'une erreur représente une situation externe généralement impossible à récupérer.

Toutes les erreurs générées par la JVM sont des instances deError ou de l'une de ses sous-classes, les plus courantes incluent mais ne sont pas limitées à:

  • OutOfMemoryError - renvoyé lorsque la machine virtuelle Java ne peut pas allouer plus d'objets car il manque de mémoire et que le garbage collector n'a pas pu en rendre plus disponible

  • StackOverflowError - se produit lorsque l'espace de pile pour un thread est épuisé, généralement parce qu'une application récurent trop profondément

  • ExceptionInInitializerError - signale qu'une exception inattendue s'est produite lors de l'évaluation d'un initialiseur statique

  • NoClassDefFoundError - est lancé lorsque le chargeur de classe essaie de charger la définition d'une classe et ne parvient pas à la trouver, généralement parce que les fichiersclass requis n'ont pas été trouvés dans le chemin de classe

  • UnsupportedClassVersionError - se produit lorsque la JVM tente de lire un fichierclass et détermine que la version du fichier n'est pas prise en charge, normalement parce que le fichier a été généré avec une version plus récente de Java

Bien qu'une erreur puisse être gérée avec une instructiontry, ce n'est pas une pratique recommandée car il n'y a aucune garantie que le programme sera capable de faire quoi que ce soit de manière fiable après que l'erreur a été générée.

Q7. Quelle exception sera levée lors de l'exécution du bloc de code suivant?

Integer[][] ints = { { 1, 2, 3 }, { null }, { 7, 8, 9 } };
System.out.println("value = " + ints[1][1].intValue());

Il lance unArrayIndexOutOfBoundsException car nous essayons d'accéder à une position supérieure à la longueur du tableau.

Q8. Qu'est-ce que l'enchaînement d'exceptions?

Se produit lorsqu'une exception est levée en réponse à une autre exception. Cela nous permet de découvrir l'histoire complète de notre problème:

try {
    task.readConfigFile();
} catch (FileNotFoundException ex) {
    throw new TaskException("Could not perform task", ex);
}

Q9. Qu'est-ce qu'un Stacktrace et comment se rapporte-t-il à une exception?

Une trace de pile fournit les noms des classes et des méthodes appelées depuis le début de l'application jusqu'au point où une exception s'est produite.

C'est un outil de débogage très utile car il nous permet de déterminer exactement où l'exception a été lancée dans l'application et les causes originales qui y ont conduit.

Q10. Pourquoi voudriez-vous sous-classer une exception?

Si le type d'exception n'est pas représenté par ceux qui existent déjà sur la plate-forme Java, ou si vous devez fournir plus d'informations au code client pour le traiter de manière plus précise, vous devez créer une exception personnalisée.

La décision d'activer ou non une exception personnalisée dépend entièrement de l'analyse de rentabilisation. Cependant, en règle générale; si on s'attend à ce que le code utilisant votre exception récupère, alors créez une exception cochée, sinon décochez-la.

De plus, vous devez hériter de la sous-classeException la plus spécifique qui est étroitement liée à celle que vous souhaitez lancer. S'il n'y a pas de telle classe, choisissezException comme parent.

Q11. Quels sont les avantages des exceptions?

Les techniques traditionnelles de détection et de traitement des erreurs conduisent souvent à des codes spaghettis difficiles à conserver et à lire. Cependant, des exceptions nous permettent de séparer la logique centrale de notre application des détails de ce qu'il faut faire en cas d'imprévu.

De plus, comme la machine virtuelle Java effectue une recherche en arrière dans la pile d'appels pour rechercher les méthodes intéressées par la gestion d'une exception particulière; nous obtenons la possibilité de propager une erreur dans la pile d'appels sans écrire de code supplémentaire.

De plus, comme toutes les exceptions levées dans un programme sont des objets, elles peuvent être regroupées ou classées en fonction de sa hiérarchie de classes. Cela nous permet d'attraper des exceptions de groupe dans un seul gestionnaire d'exceptions en spécifiant la superclasse de l'exception dans le bloccatch.

Q12. Pouvez-vous jeter une exception dans le corps d’une expression Lambda?

Lorsque vous utilisez une interface fonctionnelle standard déjà fournie par Java, vous pouvez uniquement générer des exceptions non vérifiées, car les interfaces fonctionnelles standard ne comportent pas de clause «Throws» dans les signatures de méthode:

List integers = Arrays.asList(3, 9, 7, 0, 10, 20);
integers.forEach(i -> {
    if (i == 0) {
        throw new IllegalArgumentException("Zero not allowed");
    }
    System.out.println(Math.PI / i);
});

Toutefois, si vous utilisez une interface fonctionnelle personnalisée, il est possible de lancer des exceptions vérifiées:

@FunctionalInterface
public static interface CheckedFunction {
    void apply(T t) throws Exception;
}
public void processTasks(
  List taks, CheckedFunction checkedFunction) {
    for (Task task : taks) {
        try {
            checkedFunction.apply(task);
        } catch (Exception e) {
            // ...
        }
    }
}

processTasks(taskList, t -> {
    // ...
    throw new Exception("Something happened");
});

Q13. Quelles sont les règles que nous devons suivre lors du remplacement d'une méthode qui lève une exception?

Plusieurs règles dictent comment les exceptions doivent être déclarées dans le contexte de l'héritage.

Lorsque la méthode de classe parente ne lève aucune exception, la méthode de classe enfant ne peut pas lever d'exception cochée, mais elle peut lever toute exception non cochée.

Voici un exemple de code pour illustrer ceci:

class Parent {
    void doSomething() {
        // ...
    }
}

class Child extends Parent {
    void doSomething() throws IllegalArgumentException {
        // ...
    }
}

La compilation de l'exemple suivant échouera, car la méthode de substitution lève une exception vérifiée non déclarée dans la méthode de remplacement:

class Parent {
    void doSomething() {
        // ...
    }
}

class Child extends Parent {
    void doSomething() throws IOException {
        // Compilation error
    }
}

Lorsque la méthode de classe parent lève une ou plusieurs exceptions vérifiées, la méthode de classe enfant peut lever toute exception non contrôlée. toutes, aucune ou un sous-ensemble des exceptions vérifiées déclarées, et même un nombre plus grand d’entre elles, dans la mesure où elles ont la même portée ou sont plus étroites.

Voici un exemple de code qui suit avec succès la règle précédente:

class Parent {
    void doSomething() throws IOException, ParseException {
        // ...
    }

    void doSomethingElse() throws IOException {
        // ...
    }
}

class Child extends Parent {
    void doSomething() throws IOException {
        // ...
    }

    void doSomethingElse() throws FileNotFoundException, EOFException {
        // ...
    }
}

Notez que les deux méthodes respectent la règle. La première lève moins d'exceptions que la méthode surchargée, et la seconde, même si elle en lance plus; leur portée est plus étroite.

Cependant, si nous essayons de lancer une exception vérifiée que la méthode de classe parente ne déclare pas ou si nous en lançons une avec une portée plus large; nous obtiendrons une erreur de compilation:

class Parent {
    void doSomething() throws FileNotFoundException {
        // ...
    }
}

class Child extends Parent {
    void doSomething() throws IOException {
        // Compilation error
    }
}

Lorsque la méthode de classe parent comporte une clause throws avec une exception non contrôlée, la méthode de classe enfant peut ne générer aucune exception ou un nombre quelconque d'exceptions non contrôlées, même si elles ne sont pas liées.

Voici un exemple qui respecte la règle:

class Parent {
    void doSomething() throws IllegalArgumentException {
        // ...
    }
}

class Child extends Parent {
    void doSomething()
      throws ArithmeticException, BufferOverflowException {
        // ...
    }
}

Q14. Le code suivant sera-t-il compilé?

void doSomething() {
    // ...
    throw new RuntimeException(new Exception("Chained Exception"));
}

Yes. Lors du chaînage d'exceptions, le compilateur ne se soucie que du premier de la chaîne et, comme il détecte une exception non vérifiée, nous n'avons pas besoin d'ajouter une clause throws.

Q15. Existe-t-il un moyen de lever une exception vérifiée à partir d'une méthode qui n'a pas de clause Throws?

Yes. Nous pouvons profiter de l'effacement de type effectué par le compilateur et lui faire penser que nous lançons une exception non vérifiée, alors qu'en fait; nous lançons une exception vérifiée:

public  T sneakyThrow(Throwable ex) throws T {
    throw (T) ex;
}

public void methodWithoutThrows() {
    this.sneakyThrow(new Exception("Checked Exception"));
}

3. Conclusion

Dans cet article, nous avons exploré certaines des questions susceptibles d'apparaître dans les entretiens techniques avec les développeurs Java, en ce qui concerne les exceptions. Cette liste n'est pas exhaustive et doit être considérée uniquement comme le début de recherches ultérieures.

Nous, par exemple, vous souhaitons du succès dans toutes les interviews à venir.