JavaRush /Blog Java /Random-FR /Analyse des questions et réponses des entretiens pour dév...

Analyse des questions et réponses des entretiens pour développeur Java. Partie 12

Publié dans le groupe Random-FR
Bonjour! La connaissance est le pouvoir. Plus vous aurez de connaissances avant votre premier entretien, plus vous vous sentirez en confiance. Analyse des questions et réponses des entretiens pour développeur Java.  Partie 12 - 1Avec une bonne quantité de connaissances, vous serez difficile à confondre, et en même temps vous pourrez surprendre agréablement votre interlocuteur. Par conséquent, aujourd'hui, sans plus tarder, nous continuerons à renforcer votre base théorique en examinant plus de 250 questions pour un développeur Java . Analyse des questions et réponses des entretiens pour développeur Java.  Partie 12 - 2

103. Quelles sont les règles de vérification des exceptions en matière d'héritage ?

Si je comprends bien la question, ils posent des questions sur les règles pour travailler avec des exceptions lors de l'héritage, et elles sont les suivantes :
  • Une méthode remplacée ou implémentée dans un descendant/une implémentation ne peut pas lever d'exceptions vérifiées qui sont plus élevées dans la hiérarchie que les exceptions de la méthode superclasse/interface.
Autrement dit, si nous avons une certaine interface Animal avec une méthode qui lève IOException :
public  interface Animal {
   void voice() throws IOException;
}
Dans l'implémentation de cette interface, nous ne pouvons pas lancer une exception levée plus générale (par exemple, Exception , Throwable ), mais nous pouvons la remplacer par une exception descendante, telle que FileNotFoundException :
public class Cat implements Animal {
   @Override
   public void voice() throws FileNotFoundException {
// некоторая реализация
   }
}
  • Le constructeur de sous-classe doit inclure dans son bloc throws toutes les classes d'exception levées par le constructeur de superclasse appelé lors de la création de l'objet.
Supposons que le constructeur de la classe Animal lève de nombreuses exceptions :
public class Animal {
  public Animal() throws ArithmeticException, NullPointerException, IOException {
  }
Ensuite l'héritier de classe doit également les indiquer dans le constructeur :
public class Cat extends Animal {
   public Cat() throws ArithmeticException, NullPointerException, IOException {
       super();
   }
Ou, comme dans le cas des méthodes, vous pouvez spécifier non pas les mêmes exceptions, mais des exceptions plus générales. Dans notre cas, il suffira de préciser une exception plus générale - Exception , puisqu'il s'agit de l'ancêtre commun des trois exceptions considérées :
public class Cat extends Animal {
   public Cat() throws Exception {
       super();
   }

104. Pourriez-vous écrire du code lorsque le bloc final ne sera pas exécuté ?

Tout d’abord, rappelons-nous ce qu’est finalement . Auparavant, nous avons examiné le mécanisme de capture des exceptions : le bloc try décrit la zone de capture, tandis que le ou les blocs catch sont le code qui fonctionnera lorsqu'une exception spécifique est levée. Enfin est le troisième bloc de code après enfin qui est interchangeable avec catch mais ne s'exclut pas mutuellement. L'essence de ce bloc est que le code qu'il contient fonctionne toujours, quel que soit le résultat du try ou du catch (indépendamment du fait qu'une exception ait été levée ou non). Les cas de défaillance sont très rares et anormaux. Le cas d'échec le plus simple est lorsque la méthode System.exit(0) est appelée dans le code ci-dessus , ce qui termine le programme (l'éteint) :
try {
   throw new IOException();
} catch (IOException e) {
   System.exit(0);
} finally {
   System.out.println("Данное сообщение не будет выведенно в консоль");
}
Il existe également d'autres situations dans lesquelles cela ne fonctionnera finalement pas :
  • Arrêt anormal du programme causé par des problèmes critiques du système, ou la chute d'une erreur qui fera "planter" l'application (un exemple d'erreur peut être la même StackOwerflowError qui se produit lorsque la mémoire de la pile déborde).
  • Lorsque le thread démon passe par le bloc ry...finally et en parallèle, le programme se termine. Après tout, le thread démon est un thread pour les actions en arrière-plan, c'est-à-dire qu'il n'est ni prioritaire ni obligatoire, et l'application n'attendra pas la fin de son travail.
  • La boucle infinie la plus courante, dans try ou catch , une fois dans laquelle le flux y restera pour toujours :

    try {
       while (true) {
       }
    } finally {
       System.out.println("Данное сообщение не будет выведенно в консоль");
    }

Cette question est très populaire dans les entretiens pour les débutants, c'est pourquoi quelques-unes de ces situations exceptionnelles méritent d'être rappelées. Analyse des questions et réponses des entretiens pour développeur Java.  Partie 12 - 3

105. Écrivez un exemple de gestion de plusieurs exceptions dans un seul bloc catch

1) Peut-être que la question n’a pas été posée correctement. Autant que je sache, cette question fait référence à plusieurs captures pour un bloc try :
try {
  throw new FileNotFoundException();
} catch (FileNotFoundException e) {
   System.out.print("Упс, у вас упало исключение - " + e);
} catch (IOException e) {
   System.out.print("Упс, у вас упало исключение - " + e);
} catch (Exception e) {
   System.out.print("Упс, у вас упало исключение - " + e);
}
Si une exception se produit dans un bloc try , alors les blocs catch tentent alternativement de l'attraper de haut en bas. Si un certain bloc catch réussit, il obtient le droit de gérer l'exception, tandis que le reste des blocs ci-dessous ne le seront plus. capables d'essayer de l'attraper et de le traiter à leur manière. Par conséquent, les exceptions plus étroites sont placées plus haut dans la chaîne de blocs catch , et les exceptions plus larges sont placées plus bas. Par exemple, si dans notre premier bloc catch une exception de la classe Exception est interceptée , alors les exceptions vérifiées ne pourront pas entrer dans les blocs restants (les blocs restants avec des descendants d'Exception seront absolument inutiles). 2) La question a été posée correctement. Dans ce cas, notre traitement ressemblera à ce qui suit :
try {
  throw new NullPointerException();
} catch (Exception e) {
   if (e instanceof FileNotFoundException) {
       // некоторая обработка с сужением типа (FileNotFoundException)e
   } else if (e instanceof ArithmeticException) {
       // некоторая обработка с сужением типа (ArithmeticException)e
   } else if(e instanceof NullPointerException) {
       // некоторая обработка с сужением типа (NullPointerException)e
   }
Après avoir détecté une exception via catch , nous essayons de découvrir son type spécifique via la méthode instanceof , qui est utilisée pour vérifier si un objet appartient à un certain type, afin que nous puissions plus tard le réduire à ce type sans conséquences négatives. Les deux approches envisagées peuvent être utilisées dans la même situation, mais j'ai dit que la question est incorrecte car je ne qualifierais pas la deuxième option de bonne et je ne l'ai jamais vue dans ma pratique, alors qu'en même temps la première méthode avec multi-prises s'est répandue. attention. Analyse des questions et réponses des entretiens pour développeur Java.  Partie 12 - 4

106. Quel opérateur vous permet de forcer la levée d'une exception ? Écrivez un exemple

Je l'ai déjà utilisé plusieurs fois ci-dessus, mais je répéterai néanmoins ce mot-clé - throw . Exemple d'utilisation (forcer une exception) :
throw new NullPointerException();

107. La méthode main peut-elle lever une exception throws ? Si oui, où sera-t-il transféré ?

Tout d'abord, je tiens à noter que main n'est rien de plus qu'une méthode régulière, et oui, elle est appelée par la machine virtuelle pour démarrer l'exécution du programme, mais en plus de cela, elle peut être appelée à partir de n'importe quel autre code. Autrement dit, il est également soumis aux règles habituelles pour spécifier les exceptions vérifiées après les lancements :
public static void main(String[] args) throws IOException {
En conséquence, des exceptions peuvent également s'y produire. Si main n'a pas été appelé dans une méthode, mais a été démarré comme point de lancement de programme, alors l'exception levée par celui-ci sera gérée par l' intercepteur .UncaughtExceptionHandler . Ce gestionnaire est un par thread (c'est-à-dire un gestionnaire dans chaque thread). Si nécessaire, vous pouvez créer votre propre gestionnaire et le définir à l'aide de la méthode setDefaultUncaughtExceptionHandler appelée sur l' objet Thread .

Multithreading

Analyse des questions et réponses des entretiens pour développeur Java.  Partie 12 à 5

108. Quels outils connaissez-vous pour travailler avec le multithreading ?

Outils de base/de base pour l'utilisation du multithreading en Java :
  • Synchronisé est un mécanisme permettant de fermer (bloquer) une méthode/un bloc lorsqu'un thread y entre, à partir d'autres threads.
  • Volatile est un mécanisme permettant de garantir un accès cohérent à une variable par différents threads, c'est-à-dire qu'avec la présence de ce modificateur sur une variable, toutes les opérations d'affectation et de lecture doivent être atomiques. En d’autres termes, les threads ne copieront pas cette variable dans leur mémoire locale et ne la modifieront pas, mais modifieront sa valeur d’origine.
En savoir plus sur volatile ici .
  • Runnable est une interface qui peut être implémentée (en particulier sa méthode run) dans une certaine classe :
public class CustomRunnable implements Runnable {
   @Override
   public void run() {
       // некоторая логика
   }
}
Et après avoir créé un objet de cette classe, vous pouvez démarrer un nouveau thread en définissant cet objet dans le constructeur du nouvel objet Thread et en appelant sa méthode start() :
Runnable runnable = new CustomRunnable();
new Thread(runnable).start();
La méthode start exécute la méthode run() implémentée dans un thread séparé.
  • Thread est une classe dont hérite (tout en remplaçant la méthode run ) :
public class CustomThread extends Thread {
   @Override
   public void run() {
       // некоторая логика
   }
}
Et en créant un objet de cette classe et en le lançant à l'aide de la méthode start() , nous lancerons ainsi un nouveau thread :
new CustomThread().start();
  • Concurrency est un package comprenant des outils permettant de travailler dans un environnement multithread.
Cela consiste en:
  • Concurrent Collections - un ensemble de collections spécialisées pour travailler dans un environnement multithread.
  • Files d'attente - files d'attente spécialisées pour un environnement multithread (bloquantes et non bloquantes).
  • Les synchroniseurs sont des utilitaires spécialisés permettant de travailler dans un environnement multithread.
  • Les exécuteurs sont des mécanismes permettant de créer des pools de threads.
  • Verrous - mécanismes de synchronisation des threads (plus flexibles que les mécanismes standard - synchronisé, attendre, notifier, notifierAll).
  • Les atomiques sont des classes optimisées pour une exécution multithread ; chaque opération est atomique.
En savoir plus sur le package simultané ici .

109. Parlez de synchronisation entre les threads. À quoi servent les méthodes wait(), notify() - notifyAll() join() ?

Pour autant que je comprends la question, la synchronisation entre les threads concerne le modificateur de clé - synchronisé . Ce modificateur peut être placé soit directement à côté du bloc :
synchronized (Main.class) {
   // некоторая логика
}
Ou directement dans la signature de la méthode :
public synchronized void move() {
   // некоторая логика}
Comme je l'ai dit plus tôt, synchronisé est un mécanisme qui vous permet de fermer un bloc/une méthode à partir d'autres threads lorsqu'un thread y est déjà entré. Considérez un bloc/une méthode comme une pièce. Un ruisseau, étant venu vers elle, y entrera et la verrouillera, d'autres ruisseaux, étant venus dans la pièce et voyant qu'elle est fermée, attendront près d'elle jusqu'à ce qu'elle soit libre. Ayant fait son affaire, le premier fil quitte la pièce et libère la clé. Et ce n'est pas pour rien que je parlais constamment de la clé, car elle existe vraiment. Il s'agit d'un objet spécial qui a un état occupé/libre. Cet objet est attaché à chaque objet Java, donc lors de l'utilisation d'un bloc synchronisé , nous devons indiquer entre parenthèses l'objet dont nous voulons fermer la porte avec le mutex :
Cat cat = new Cat();
synchronized (cat) {
   // некоторая логика
}
Vous pouvez également utiliser un mutex de classe, comme je l'ai fait dans le premier exemple ( Main.class ). Lorsqu’on utilise synchronisé sur une méthode, on ne précise pas l’objet sur lequel on veut fermer, non ? Dans ce cas, pour une méthode non statique, elle se fermera sur le mutex de cet objet , c'est-à-dire l'objet actuel de cette classe. Le statique se fermera sur le mutex de la classe actuelle ( this.getClass(); ). Vous pouvez en savoir plus sur le mutex ici . Eh bien, lisez à propos de la synchronisation ici . Wait() est une méthode qui libère le mutex et met le thread actuel en mode veille, comme s'il était attaché au moniteur actuel (quelque chose comme une ancre). Pour cette raison, cette méthode ne peut être appelée qu'à partir d'un bloc ou d'une méthode synchronisée (sinon, que devrait-elle libérer et à quoi devrait-elle s'attendre). Notez également qu'il s'agit d'une méthode de la classe Object . Plus précisément, pas un, mais même trois :
  • Wait() - met le thread actuel en mode attente jusqu'à ce qu'un autre thread appelle la méthode notify() ou notifyAll() pour cet objet (nous parlerons de ces méthodes plus tard).

  • Wait (long timeout) - met le thread actuel en mode attente jusqu'à ce qu'un autre thread appelle la méthode notify() ou notifyAll() sur cet objet ou que le délai d'attente spécifié expire .

  • Attendre (long timeout, int nanos) - similaire au précédent, seul nanos vous permet de spécifier des nanosecondes (réglage du temps plus précis).

  • Notify() est une méthode qui vous permet de réveiller un thread aléatoire du bloc de synchronisation actuel. Encore une fois, il ne peut être appelé que dans un bloc ou une méthode synchronisé (après tout, ailleurs, il n'y aura personne à dégeler).

  • NotifyAll() est une méthode qui réveille tous les threads en attente sur le moniteur actuel (également utilisée uniquement dans un bloc ou une méthode synchronisée ).

110. Comment arrêter le flux ?

La première chose à dire est que lorsque la méthode run() est entièrement exécutée , le thread est automatiquement détruit. Mais parfois, vous devez le tuer plus tôt que prévu, avant que cette méthode ne soit terminée. Alors que devrions-nous faire alors ? Peut-être que l' objet Thread devrait avoir une méthode stop() ? Peu importe comment c'est ! Cette méthode est considérée comme obsolète et peut entraîner des pannes du système. Analyse des questions et réponses des entretiens pour développeur Java.  Partie 12 à 6Eh bien, et alors ? Il existe deux manières de procéder : La première consiste à utiliser votre indicateur booléen interne. Regardons un exemple. Nous avons notre propre implémentation d'un fil de discussion qui devrait afficher une certaine phrase à l'écran jusqu'à ce qu'elle s'arrête complètement :
public class CustomThread extends Thread {
private boolean isActive;

   public CustomThread() {
       this.isActive = true;
   }

   @Override
   public void run() {
       {
           while (isActive) {
               System.out.println("Поток выполняет некую логику...");
           }
           System.out.println("Поток остановлен!");
       }
   }

   public void stopRunningThread() {
       isActive = false;
   }
}
Lors de l'utilisation de la méthode stopRunning() , l'indicateur interne devient faux et la méthode run cesse de s'exécuter. Exécutons-le dans main :
System.out.println("Начало выполнения программы");
CustomThread thread = new CustomThread();
thread.start();
Thread.sleep(3);
// пока наш основной поток спит, вспомогательный  CustomThread работает и выводит в коноль своё сообщение
thread.stopRunningThread();
System.out.println("Конец выполнения программы");
En conséquence, nous verrons quelque chose comme ceci dans la console :
Début de l'exécution du programme Le thread exécute de la logique... Le thread exécute de la logique... Le thread exécute de la logique... Le thread exécute de la logique... Le thread exécute de la logique... Le le thread exécute une logique... Fin de l'exécution du programme Le thread est arrêté !
Cela signifie que notre thread a fonctionné, a généré un certain nombre de messages sur la console et a été arrêté avec succès. Je note que le nombre de messages générés varie d'une exécution à l'autre ; parfois, le thread supplémentaire n'a même rien généré. Comme je l'ai remarqué, cela dépend du temps de veille du thread principal, plus il est long, moins il y a de chances que le thread supplémentaire ne produise rien. Avec un temps de veille de 1 ms, les messages ne sont presque jamais émis, mais si vous le réglez sur 20 ms, cela fonctionne presque toujours. Peut-être que lorsque le temps presse, le fil n'a tout simplement pas le temps de démarrer et de commencer son travail et est immédiatement arrêté. La deuxième façon consiste à utiliser la méthode interrompue() sur l'objet Thread , qui renvoie la valeur de l'indicateur d'interruption interne (ce drapeau est faux par défaut ) et son autre méthode interrompue() , qui définit cet indicateur sur true (lorsque ce l'indicateur est vrai , le thread devrait arrêter son travail). Regardons un exemple :
public class CustomThread extends Thread {

   @Override
   public void run() {
       {
           while (!Thread.interrupted()) {
               System.out.println("Поток выполняет некую логику...");
           }
           System.out.println("Поток остановлен!");
       }
   }
}
Exécuter en principal :
System.out.println("Начало выполнения программы");
Thread thread = new CustomThread();
thread.start();
Thread.sleep(3);
thread.interrupt();
System.out.println("Конец выполнения программы");
Le résultat de l'exécution sera le même que dans le premier cas, mais j'aime mieux cette approche : nous écrivons moins de code et utilisons davantage de fonctionnalités standard toutes faites. Analyse des questions et réponses des entretiens pour développeur Java.  Partie 12 à 7C'est là que nous nous arrêterons aujourd'hui.Analyse des questions et réponses des entretiens pour développeur Java.  Partie 12 à 8
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION